aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/attribute_assignment.rb1
-rw-r--r--activemodel/lib/active_model/attribute_set.rb2
-rw-r--r--activemodel/lib/active_model/attributes.rb2
-rw-r--r--activemodel/lib/active_model/callbacks.rb1
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/error.rb96
-rw-r--r--activemodel/lib/active_model/errors.rb99
-rw-r--r--activemodel/lib/active_model/naming.rb1
-rw-r--r--activemodel/lib/active_model/railtie.rb2
-rw-r--r--activemodel/lib/active_model/serialization.rb1
-rw-r--r--activemodel/lib/active_model/type/big_integer.rb1
-rw-r--r--activemodel/lib/active_model/type/boolean.rb1
-rw-r--r--activemodel/lib/active_model/type/date.rb1
-rw-r--r--activemodel/lib/active_model/type/date_time.rb1
-rw-r--r--activemodel/lib/active_model/type/decimal.rb1
-rw-r--r--activemodel/lib/active_model/type/float.rb1
-rw-r--r--activemodel/lib/active_model/type/helpers/numeric.rb1
-rw-r--r--activemodel/lib/active_model/type/helpers/time_value.rb9
-rw-r--r--activemodel/lib/active_model/type/immutable_string.rb1
-rw-r--r--activemodel/lib/active_model/type/string.rb1
-rw-r--r--activemodel/lib/active_model/type/time.rb1
-rw-r--r--activemodel/lib/active_model/type/value.rb3
-rw-r--r--activemodel/lib/active_model/validations.rb1
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb1
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb1
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb1
-rw-r--r--activemodel/lib/active_model/validations/format.rb1
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb1
-rw-r--r--activemodel/lib/active_model/validations/validates.rb1
-rw-r--r--activemodel/lib/active_model/validator.rb1
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb1
-rw-r--r--activemodel/test/cases/errors_test.rb68
-rw-r--r--activemodel/test/cases/railtie_test.rb6
-rw-r--r--activemodel/test/cases/type/date_time_test.rb1
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb99
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb1
38 files changed, 239 insertions, 176 deletions
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index c9140dc582..756473e38d 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -53,6 +53,7 @@ module ActiveModel
eager_autoload do
autoload :Errors
+ autoload :Error
autoload :RangeError, "active_model/errors"
autoload :StrictValidationFailed, "active_model/errors"
autoload :UnknownAttributeError, "active_model/errors"
diff --git a/activemodel/lib/active_model/attribute_assignment.rb b/activemodel/lib/active_model/attribute_assignment.rb
index f0e3458f51..9bdec0dfda 100644
--- a/activemodel/lib/active_model/attribute_assignment.rb
+++ b/activemodel/lib/active_model/attribute_assignment.rb
@@ -38,7 +38,6 @@ module ActiveModel
alias attributes= assign_attributes
private
-
def _assign_attributes(attributes)
attributes.each do |k, v|
_assign_attribute(k, v)
diff --git a/activemodel/lib/active_model/attribute_set.rb b/activemodel/lib/active_model/attribute_set.rb
index 4679b33852..2f5437ceda 100644
--- a/activemodel/lib/active_model/attribute_set.rb
+++ b/activemodel/lib/active_model/attribute_set.rb
@@ -94,11 +94,9 @@ module ActiveModel
end
protected
-
attr_reader :attributes
private
-
def initialized_attributes
attributes.select { |_, attr| attr.initialized? }
end
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
index d176ea88d0..c586f52b78 100644
--- a/activemodel/lib/active_model/attributes.rb
+++ b/activemodel/lib/active_model/attributes.rb
@@ -42,7 +42,6 @@ module ActiveModel
end
private
-
def define_method_attribute=(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name, writer: true,
@@ -114,7 +113,6 @@ module ActiveModel
end
private
-
def write_attribute(attr_name, value)
name = attr_name.to_s
name = self.class.attribute_aliases[name] || name
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index ea2ed7dff7..7a544395cb 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -126,7 +126,6 @@ module ActiveModel
end
private
-
def _define_before_model_callback(klass, callback)
klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
options.assert_valid_keys(:if, :unless, :prepend)
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 35a587658c..aaefe00c83 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -136,7 +136,7 @@ module ActiveModel
@mutations_from_database = nil
end
- # Clears dirty data and moves +changes+ to +previously_changed+ and
+ # Clears dirty data and moves +changes+ to +previous_changes+ and
# +mutations_from_database+ to +mutations_before_last_save+ respectively.
def changes_applied
unless defined?(@attributes)
diff --git a/activemodel/lib/active_model/error.rb b/activemodel/lib/active_model/error.rb
index 5a1298e27f..6deab3578d 100644
--- a/activemodel/lib/active_model/error.rb
+++ b/activemodel/lib/active_model/error.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/core_ext/class/attribute"
+
module ActiveModel
# == Active \Model \Error
#
@@ -8,6 +10,89 @@ module ActiveModel
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
MESSAGE_OPTIONS = [:message]
+ class_attribute :i18n_customize_full_message, default: false
+
+ def self.full_message(attribute, message, base_class) # :nodoc:
+ return message if attribute == :base
+ attribute = attribute.to_s
+
+ if i18n_customize_full_message && base_class.respond_to?(:i18n_scope)
+ attribute = attribute.remove(/\[\d\]/)
+ parts = attribute.split(".")
+ attribute_name = parts.pop
+ namespace = parts.join("/") unless parts.empty?
+ attributes_scope = "#{base_class.i18n_scope}.errors.models"
+
+ if namespace
+ defaults = base_class.lookup_ancestors.map do |klass|
+ [
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
+ ]
+ end
+ else
+ defaults = base_class.lookup_ancestors.map do |klass|
+ [
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
+ ]
+ end
+ end
+
+ defaults.flatten!
+ else
+ defaults = []
+ end
+
+ defaults << :"errors.format"
+ defaults << "%{attribute} %{message}"
+
+ attr_name = attribute.tr(".", "_").humanize
+ attr_name = base_class.human_attribute_name(attribute, default: attr_name)
+
+ I18n.t(defaults.shift,
+ default: defaults,
+ attribute: attr_name,
+ message: message)
+ end
+
+ def self.generate_message(attribute, type, base, options) # :nodoc:
+ type = options.delete(:message) if options[:message].is_a?(Symbol)
+ value = (attribute != :base ? base.send(:read_attribute_for_validation, attribute) : nil)
+
+ options = {
+ model: base.model_name.human,
+ attribute: base.class.human_attribute_name(attribute),
+ value: value,
+ object: base
+ }.merge!(options)
+
+ if base.class.respond_to?(:i18n_scope)
+ i18n_scope = base.class.i18n_scope.to_s
+ defaults = base.class.lookup_ancestors.flat_map do |klass|
+ [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
+ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ end
+ defaults << :"#{i18n_scope}.errors.messages.#{type}"
+
+ catch(:exception) do
+ translation = I18n.translate(defaults.first, options.merge(default: defaults.drop(1), throw: true))
+ return translation unless translation.nil?
+ end unless options[:message]
+ else
+ defaults = []
+ end
+
+ defaults << :"errors.attributes.#{attribute}.#{type}"
+ defaults << :"errors.messages.#{type}"
+
+ key = defaults.shift
+ defaults = options.delete(:message) if options[:message]
+ options[:default] = defaults
+
+ I18n.translate(key, options)
+ end
+
def initialize(base, attribute, type = :invalid, **options)
@base = base
@attribute = attribute
@@ -28,7 +113,7 @@ module ActiveModel
def message
case raw_type
when Symbol
- base.errors.generate_message(attribute, raw_type, options.except(*CALLBACKS_OPTIONS))
+ self.class.generate_message(attribute, raw_type, @base, options.except(*CALLBACKS_OPTIONS))
else
raw_type
end
@@ -39,7 +124,7 @@ module ActiveModel
end
def full_message
- base.errors.full_message(attribute, message)
+ self.class.full_message(attribute, message, @base.class)
end
# See if error matches provided +attribute+, +type+ and +options+.
@@ -58,9 +143,9 @@ module ActiveModel
end
def strict_match?(attribute, type, **options)
- return false unless match?(attribute, type, **options)
+ return false unless match?(attribute, type)
- full_message == Error.new(@base, attribute, type, **options).full_message
+ options == @options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS)
end
def ==(other)
@@ -73,9 +158,8 @@ module ActiveModel
end
protected
-
def attributes_for_hash
- [@base, @attribute, @raw_type, @options]
+ [@base, @attribute, @raw_type, @options.except(*CALLBACKS_OPTIONS)]
end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d7bcfacce3..42c004ce31 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/deep_dup"
require "active_support/core_ext/string/filters"
-require "active_support/deprecation"
require "active_model/error"
require "active_model/nested_error"
require "forwardable"
@@ -70,11 +69,6 @@ module ActiveModel
LEGACY_ATTRIBUTES = [:messages, :details].freeze
- class << self
- attr_accessor :i18n_customize_full_message # :nodoc:
- end
- self.i18n_customize_full_message = false
-
attr_reader :errors
alias :objects :errors
@@ -199,7 +193,7 @@ module ActiveModel
matches.each do |error|
@errors.delete(error)
end
- matches.map(&:message)
+ matches.map(&:message).presence
end
# When passed a symbol or a name of a method, returns an array of errors
@@ -226,7 +220,7 @@ module ActiveModel
# # then yield :name and "must be specified"
# end
def each(&block)
- if block.arity == 1
+ if block.arity <= 1
@errors.each(&block)
else
ActiveSupport::Deprecation.warn(<<~MSG)
@@ -309,6 +303,16 @@ module ActiveModel
hash
end
+ def to_h
+ ActiveSupport::Deprecation.warn(<<~EOM)
+ ActiveModel::Errors#to_h is deprecated and will be removed in Rails 6.2
+ Please use `ActiveModel::Errors.to_hash` instead. The values in the hash
+ returned by `ActiveModel::Errors.to_hash` is an array of error messages.
+ EOM
+
+ to_hash.transform_values { |values| values.last }
+ end
+
def messages
DeprecationHandlingMessageHash.new(self)
end
@@ -468,47 +472,7 @@ module ActiveModel
#
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
def full_message(attribute, message)
- return message if attribute == :base
- attribute = attribute.to_s
-
- if self.class.i18n_customize_full_message && @base.class.respond_to?(:i18n_scope)
- attribute = attribute.remove(/\[\d\]/)
- parts = attribute.split(".")
- attribute_name = parts.pop
- namespace = parts.join("/") unless parts.empty?
- attributes_scope = "#{@base.class.i18n_scope}.errors.models"
-
- if namespace
- defaults = @base.class.lookup_ancestors.map do |klass|
- [
- :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
- :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
- ]
- end
- else
- defaults = @base.class.lookup_ancestors.map do |klass|
- [
- :"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
- :"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
- ]
- end
- end
-
- defaults.flatten!
- else
- defaults = []
- end
-
- defaults << :"errors.format"
- defaults << "%{attribute} %{message}"
-
- attr_name = attribute.tr(".", "_").humanize
- attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
-
- I18n.t(defaults.shift,
- default: defaults,
- attribute: attr_name,
- message: message)
+ Error.full_message(attribute, message, @base.class)
end
# Translates an error message in its default scope
@@ -536,40 +500,7 @@ module ActiveModel
# * <tt>errors.attributes.title.blank</tt>
# * <tt>errors.messages.blank</tt>
def generate_message(attribute, type = :invalid, options = {})
- type = options.delete(:message) if options[:message].is_a?(Symbol)
- value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
-
- options = {
- model: @base.model_name.human,
- attribute: @base.class.human_attribute_name(attribute),
- value: value,
- object: @base
- }.merge!(options)
-
- if @base.class.respond_to?(:i18n_scope)
- i18n_scope = @base.class.i18n_scope.to_s
- defaults = @base.class.lookup_ancestors.flat_map do |klass|
- [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
- :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
- end
- defaults << :"#{i18n_scope}.errors.messages.#{type}"
-
- catch(:exception) do
- translation = I18n.translate(defaults.first, options.merge(default: defaults.drop(1), throw: true))
- return translation unless translation.nil?
- end unless options[:message]
- else
- defaults = []
- end
-
- defaults << :"errors.attributes.#{attribute}.#{type}"
- defaults << :"errors.messages.#{type}"
-
- key = defaults.shift
- defaults = options.delete(:message) if options[:message]
- options[:default] = defaults
-
- I18n.translate(key, options)
+ Error.generate_message(attribute, type, @base, options)
end
def marshal_load(array) # :nodoc:
@@ -594,7 +525,6 @@ module ActiveModel
end
private
-
def normalize_arguments(attribute, type, **options)
# Evaluate proc first
if type.respond_to?(:call)
@@ -640,7 +570,6 @@ module ActiveModel
end
private
-
def prepare_content
content = @errors.to_hash
content.each do |attribute, value|
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index bf23fa3c05..6a02d5dbf7 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -207,7 +207,6 @@ module ActiveModel
end
private
-
def _singularize(string)
ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb
index eb7901c7e9..65e20b9791 100644
--- a/activemodel/lib/active_model/railtie.rb
+++ b/activemodel/lib/active_model/railtie.rb
@@ -14,7 +14,7 @@ module ActiveModel
end
initializer "active_model.i18n_customize_full_message" do
- ActiveModel::Errors.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
+ ActiveModel::Error.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
end
end
end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index c4b7b32291..168dfa0dd2 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -150,7 +150,6 @@ module ActiveModel
end
private
-
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after
# the attribute. Override this method in subclasses should you need to
diff --git a/activemodel/lib/active_model/type/big_integer.rb b/activemodel/lib/active_model/type/big_integer.rb
index 89e43bcc5f..b2c3ee50aa 100644
--- a/activemodel/lib/active_model/type/big_integer.rb
+++ b/activemodel/lib/active_model/type/big_integer.rb
@@ -6,7 +6,6 @@ module ActiveModel
module Type
class BigInteger < Integer # :nodoc:
private
-
def max_value
::Float::INFINITY
end
diff --git a/activemodel/lib/active_model/type/boolean.rb b/activemodel/lib/active_model/type/boolean.rb
index e64d2c793c..1214e9319b 100644
--- a/activemodel/lib/active_model/type/boolean.rb
+++ b/activemodel/lib/active_model/type/boolean.rb
@@ -34,7 +34,6 @@ module ActiveModel
end
private
-
def cast_value(value)
if value == ""
nil
diff --git a/activemodel/lib/active_model/type/date.rb b/activemodel/lib/active_model/type/date.rb
index c5fe926039..0e96d2c8a4 100644
--- a/activemodel/lib/active_model/type/date.rb
+++ b/activemodel/lib/active_model/type/date.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def cast_value(value)
if value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/date_time.rb b/activemodel/lib/active_model/type/date_time.rb
index 133410e821..ba705be9b2 100644
--- a/activemodel/lib/active_model/type/date_time.rb
+++ b/activemodel/lib/active_model/type/date_time.rb
@@ -14,7 +14,6 @@ module ActiveModel
end
private
-
def cast_value(value)
return apply_seconds_precision(value) unless value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/decimal.rb b/activemodel/lib/active_model/type/decimal.rb
index e8ee18c00e..6aa51ff2ac 100644
--- a/activemodel/lib/active_model/type/decimal.rb
+++ b/activemodel/lib/active_model/type/decimal.rb
@@ -17,7 +17,6 @@ module ActiveModel
end
private
-
def cast_value(value)
casted_value = \
case value
diff --git a/activemodel/lib/active_model/type/float.rb b/activemodel/lib/active_model/type/float.rb
index ea1987df7c..36c7a5103c 100644
--- a/activemodel/lib/active_model/type/float.rb
+++ b/activemodel/lib/active_model/type/float.rb
@@ -19,7 +19,6 @@ module ActiveModel
end
private
-
def cast_value(value)
case value
when ::Float then value
diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb
index 1d8171e25b..074316b559 100644
--- a/activemodel/lib/active_model/type/helpers/numeric.rb
+++ b/activemodel/lib/active_model/type/helpers/numeric.rb
@@ -24,7 +24,6 @@ module ActiveModel
end
private
-
def number_to_non_number?(old_value, new_value_before_type_cast)
old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
end
diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb
index dab196d653..075e906034 100644
--- a/activemodel/lib/active_model/type/helpers/time_value.rb
+++ b/activemodel/lib/active_model/type/helpers/time_value.rb
@@ -11,10 +11,10 @@ module ActiveModel
value = apply_seconds_precision(value)
if value.acts_like?(:time)
- zone_conversion_method = is_utc? ? :getutc : :getlocal
-
- if value.respond_to?(zone_conversion_method)
- value = value.send(zone_conversion_method)
+ if is_utc?
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+ else
+ value = value.getlocal if value.respond_to?(:getlocal)
end
end
@@ -44,7 +44,6 @@ module ActiveModel
end
private
-
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
# Treat 0000-00-00 00:00:00 as nil.
return if year.nil? || (year == 0 && mon == 0 && mday == 0)
diff --git a/activemodel/lib/active_model/type/immutable_string.rb b/activemodel/lib/active_model/type/immutable_string.rb
index 826bd7038f..18e12c54d1 100644
--- a/activemodel/lib/active_model/type/immutable_string.rb
+++ b/activemodel/lib/active_model/type/immutable_string.rb
@@ -17,7 +17,6 @@ module ActiveModel
end
private
-
def cast_value(value)
result = \
case value
diff --git a/activemodel/lib/active_model/type/string.rb b/activemodel/lib/active_model/type/string.rb
index a9c9bfadb6..0d9f4a63b4 100644
--- a/activemodel/lib/active_model/type/string.rb
+++ b/activemodel/lib/active_model/type/string.rb
@@ -12,7 +12,6 @@ module ActiveModel
end
private
-
def cast_value(value)
case value
when ::String then ::String.new(value)
diff --git a/activemodel/lib/active_model/type/time.rb b/activemodel/lib/active_model/type/time.rb
index 61847a4ce7..f230bd4257 100644
--- a/activemodel/lib/active_model/type/time.rb
+++ b/activemodel/lib/active_model/type/time.rb
@@ -29,7 +29,6 @@ module ActiveModel
end
private
-
def cast_value(value)
return apply_seconds_precision(value) unless value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/value.rb b/activemodel/lib/active_model/type/value.rb
index b6914dd63c..788ded3e96 100644
--- a/activemodel/lib/active_model/type/value.rb
+++ b/activemodel/lib/active_model/type/value.rb
@@ -110,11 +110,10 @@ module ActiveModel
[self.class, precision, scale, limit].hash
end
- def assert_valid_value(*)
+ def assert_valid_value(_)
end
private
-
# Convenience method for types which do not need separate type casting
# behavior for user and database inputs. Called by Value#cast for
# values except +nil+.
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index f18f9a601a..4a6b464131 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -402,7 +402,6 @@ module ActiveModel
alias :read_attribute_for_validation :send
private
-
def run_validations!
_run_validate_callbacks
errors.empty?
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 6fd54270f2..1b96575a10 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def setup!(klass)
klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
end
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index 887d31ae2a..7178ba99e9 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -112,7 +112,6 @@ module ActiveModel
end
private
-
# Overwrite run validations to include callbacks.
def run_validations!
_run_validation_callbacks { super }
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index bafb8e2106..fb9f48301c 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def include?(record, value)
members = if delimiter.respond_to?(:call)
delimiter.call(record)
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 7c3f091473..bea57415b0 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -23,7 +23,6 @@ module ActiveModel
end
private
-
def option_call(record, name)
option = options[name]
option.respond_to?(:call) ? option.call(record) : option
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 51e224d5cd..e7be668e9d 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -79,7 +79,6 @@ module ActiveModel
end
private
-
def is_number?(raw_value)
!parse_as_number(raw_value).nil?
rescue ArgumentError, TypeError
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 21c4ce0dfe..97612d474d 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -150,7 +150,6 @@ module ActiveModel
end
private
-
# When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method.
def _validates_default_keys
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 94d53b8dd1..3ba6acea15 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -175,7 +175,6 @@ module ActiveModel
end
private
-
def validate_each(record, attribute, value)
@block.call(record, attribute, value)
end
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index 4e228032c3..e3b3b15f25 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -40,7 +40,6 @@ private
end
protected
-
def protected_method
"O_o O_o"
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index baaf404f2e..a6cd1da717 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -44,6 +44,14 @@ class ErrorsTest < ActiveModel::TestCase
assert_includes errors, "foo", "errors should include 'foo' as :foo"
end
+ def test_each_when_arity_is_negative
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :blank)
+ errors.add(:gender, :blank)
+
+ assert_equal([:name, :gender], errors.map(&:attribute))
+ end
+
def test_any?
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name)
@@ -274,6 +282,28 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal [msg], person.errors[:name]
end
+ test "added? when attribute was added through a collection" do
+ person = Person.new
+ person.errors.add(:"family_members.name", :too_long, count: 25)
+ assert person.errors.added?(:"family_members.name", :too_long, count: 25)
+ assert_not person.errors.added?(:"family_members.name", :too_long)
+ assert_not person.errors.added?(:"family_members.name", :too_long, name: "hello")
+ end
+
+ test "added? ignores callback option" do
+ person = Person.new
+
+ person.errors.add(:name, :too_long, if: -> { true })
+ assert person.errors.added?(:name, :too_long)
+ end
+
+ test "added? ignores message option" do
+ person = Person.new
+
+ person.errors.add(:name, :too_long, message: proc { "foo" })
+ assert person.errors.added?(:name, :too_long)
+ end
+
test "added? detects indifferent if a specific error was added to the object" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -444,6 +474,17 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a
end
+ test "to_h is deprecated" do
+ person = Person.new
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:name, "too long")
+
+ expected_deprecation = "ActiveModel::Errors#to_h is deprecated"
+ assert_deprecated(expected_deprecation) do
+ assert_equal({ name: "too long" }, person.errors.to_h)
+ end
+ end
+
test "to_hash returns the error messages hash" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -460,6 +501,27 @@ class ErrorsTest < ActiveModel::TestCase
assert_nil person.errors.as_json.default_proc
end
+ test "full_messages doesn't require the base object to respond to `:errors" do
+ model = Class.new do
+ def initialize
+ @errors = ActiveModel::Errors.new(self)
+ @errors.add(:name, "bar")
+ end
+
+ def self.human_attribute_name(attr, options = {})
+ "foo"
+ end
+
+ def call
+ error_wrapper = Struct.new(:model_errors)
+
+ error_wrapper.new(@errors)
+ end
+ end
+
+ assert_equal(["foo bar"], model.new.call.model_errors.full_messages)
+ end
+
test "full_messages creates a list of error messages with the attribute name included" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -573,6 +635,12 @@ class ErrorsTest < ActiveModel::TestCase
assert_not_equal errors_dup.details, errors.details
end
+ test "delete returns nil when no errors were deleted" do
+ errors = ActiveModel::Errors.new(Person.new)
+
+ assert_nil(errors.delete(:name))
+ end
+
test "delete removes details on given attribute" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb
index 95ee7cace3..f5ff1a3fd7 100644
--- a/activemodel/test/cases/railtie_test.rb
+++ b/activemodel/test/cases/railtie_test.rb
@@ -35,20 +35,20 @@ class RailtieTest < ActiveModel::TestCase
test "i18n customize full message defaults to false" do
@app.initialize!
- assert_equal false, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal false, ActiveModel::Error.i18n_customize_full_message
end
test "i18n customize full message can be disabled" do
@app.config.active_model.i18n_customize_full_message = false
@app.initialize!
- assert_equal false, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal false, ActiveModel::Error.i18n_customize_full_message
end
test "i18n customize full message can be enabled" do
@app.config.active_model.i18n_customize_full_message = true
@app.initialize!
- assert_equal true, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal true, ActiveModel::Error.i18n_customize_full_message
end
end
diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb
index 74b47d1b4d..4a63eee0cf 100644
--- a/activemodel/test/cases/type/date_time_test.rb
+++ b/activemodel/test/cases/type/date_time_test.rb
@@ -37,7 +37,6 @@ module ActiveModel
end
private
-
def with_timezone_config(default:)
old_zone_default = ::Time.zone_default
::Time.zone_default = ::Time.find_zone(default)
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index 72baf6e7a7..6bd3d292f8 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -92,7 +92,6 @@ class AcceptanceValidationTest < ActiveModel::TestCase
end
private
-
# Acceptance validator includes anonymous module into class, which cannot
# be cleared, so to avoid multiple inclusions we use a named subclass which
# we can remove in teardown.
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index b7ee50832c..c81649f493 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -13,8 +13,8 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.backend = I18n::Backend::Simple.new
I18n.backend.store_translations("en", errors: { messages: { custom: nil } })
- @original_i18n_customize_full_message = ActiveModel::Errors.i18n_customize_full_message
- ActiveModel::Errors.i18n_customize_full_message = true
+ @original_i18n_customize_full_message = ActiveModel::Error.i18n_customize_full_message
+ ActiveModel::Error.i18n_customize_full_message = true
end
def teardown
@@ -24,7 +24,7 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
I18n.backend.reload!
- ActiveModel::Errors.i18n_customize_full_message = @original_i18n_customize_full_message
+ ActiveModel::Error.i18n_customize_full_message = @original_i18n_customize_full_message
end
def test_full_message_encoding
@@ -49,7 +49,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_doesnt_use_attribute_format_without_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } })
@@ -59,8 +59,21 @@ class I18nValidationTest < ActiveModel::TestCase
assert_equal "Name test cannot be blank", person.errors.full_message(:name_test, "cannot be blank")
end
+ def test_errors_full_messages_on_nested_error_uses_attribute_format
+ ActiveModel::Error.i18n_customize_full_message = true
+ I18n.backend.store_translations("en", activemodel: {
+ errors: { models: { person: { attributes: { gender: "Gender" } } } },
+ attributes: { "person/contacts": { gender: "Gender" } }
+ })
+
+ person = person_class.new
+ error = ActiveModel::Error.new(person, :gender, "can't be blank")
+ person.errors.import(error, attribute: "person[0].contacts.gender")
+ assert_equal ["Gender can't be blank"], person.errors.full_messages
+ end
+
def test_errors_full_messages_uses_attribute_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } })
@@ -71,7 +84,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { format: "%{message}" } } } })
@@ -82,7 +95,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_deeply_nested_model_attributes_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -93,7 +106,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_deeply_nested_model_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } })
@@ -104,7 +117,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_attributes_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -115,7 +128,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } })
@@ -126,7 +139,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_i18n_attribute_name
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
attributes: { 'person/contacts/addresses': { country: "Country" } }
@@ -138,7 +151,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_without_i18n_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -149,7 +162,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_i18n_attribute_name_without_i18n_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
attributes: { 'person/contacts[0]/addresses[0]': { country: "Country" } }
@@ -178,8 +191,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_confirmation_of on generated message #{name}" do
person_class.validates_confirmation_of :title, validation_options
@person.title_confirmation = "foo"
- call = [:title_confirmation, :confirmation, generate_message_options.merge(attribute: "Title")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title_confirmation, :confirmation, @person, generate_message_options.merge(attribute: "Title")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -189,8 +202,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_acceptance_of on generated message #{name}" do
person_class.validates_acceptance_of :title, validation_options.merge(allow_nil: false)
- call = [:title, :accepted, generate_message_options]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :accepted, @person, generate_message_options]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -200,8 +213,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_presence_of on generated message #{name}" do
person_class.validates_presence_of :title, validation_options
- call = [:title, :blank, generate_message_options]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :blank, @person, generate_message_options]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -211,8 +224,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :within on generated message when too short #{name}" do
person_class.validates_length_of :title, validation_options.merge(within: 3..5)
- call = [:title, :too_short, generate_message_options.merge(count: 3)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :too_short, @person, generate_message_options.merge(count: 3)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -223,8 +236,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_length_of for :too_long generated message #{name}" do
person_class.validates_length_of :title, validation_options.merge(within: 3..5)
@person.title = "this title is too long"
- call = [:title, :too_long, generate_message_options.merge(count: 5)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :too_long, @person, generate_message_options.merge(count: 5)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -234,8 +247,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :is on generated message #{name}" do
person_class.validates_length_of :title, validation_options.merge(is: 5)
- call = [:title, :wrong_length, generate_message_options.merge(count: 5)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :wrong_length, @person, generate_message_options.merge(count: 5)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -246,8 +259,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_format_of on generated message #{name}" do
person_class.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/)
@person.title = "72x"
- call = [:title, :invalid, generate_message_options.merge(value: "72x")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :invalid, @person, generate_message_options.merge(value: "72x")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -258,8 +271,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of on generated message #{name}" do
person_class.validates_inclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = "z"
- call = [:title, :inclusion, generate_message_options.merge(value: "z")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -270,8 +283,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of using :within on generated message #{name}" do
person_class.validates_inclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = "z"
- call = [:title, :inclusion, generate_message_options.merge(value: "z")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -282,8 +295,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of generated message #{name}" do
person_class.validates_exclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = "a"
- call = [:title, :exclusion, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -294,8 +307,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of using :within generated message #{name}" do
person_class.validates_exclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = "a"
- call = [:title, :exclusion, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -306,8 +319,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of generated message #{name}" do
person_class.validates_numericality_of :title, validation_options
@person.title = "a"
- call = [:title, :not_a_number, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :not_a_number, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -318,8 +331,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :only_integer on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true)
@person.title = "0.0"
- call = [:title, :not_an_integer, generate_message_options.merge(value: "0.0")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :not_an_integer, @person, generate_message_options.merge(value: "0.0")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -330,8 +343,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :odd on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true)
@person.title = 0
- call = [:title, :odd, generate_message_options.merge(value: 0)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :odd, @person, generate_message_options.merge(value: 0)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
@@ -342,8 +355,8 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :less_than on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0)
@person.title = 1
- call = [:title, :less_than, generate_message_options.merge(value: 1, count: 0)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :less_than, @person, generate_message_options.merge(value: 1, count: 0)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
@person.errors.messages
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index 16c44762cb..191af033df 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -310,7 +310,6 @@ class NumericalityValidationTest < ActiveModel::TestCase
end
private
-
def invalid!(values, error = nil)
with_each_topic_approved_value(values) do |topic, value|
assert topic.invalid?, "#{value.inspect} not rejected as a number"