There is a form class created using ActiveModel
hoge_form.rb
class HogeForm
include ActiveModel::Model
include ActiveModel::Attributes
include ActiveModel::AttributeMethods
attribute :number, :integer
validates :number, numericality: { only_integer: true }
end
I want to make a validation error when a value other than an integer value comes for number
Verification
form = HogeForm.new
form.number = 'hoge'
form.valid?
# => true
form.number
# => 0
Substituting hoge
will internally cast to integer
and will not result in a validation error
Doing the same with ActiveRecord
will result in an error as it validates the value before casting.
Expect an error in ActiveModel
as well
Click here for the source code of the implementation of the validation of numericality
of ActiveModel
.
(https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/numericality.rb)
Cite a method to specify the value to use for validation validation
activemodel/lib/active_model/validations/numericality.rb
def prepare_value_for_validation(value, record, attr_name)
return value if record_attribute_changed_in_place?(record, attr_name)
came_from_user = :"#{attr_name}_came_from_user?"
if record.respond_to?(came_from_user)
if record.public_send(came_from_user)
raw_value = record.public_send(:"#{attr_name}_before_type_cast")
elsif record.respond_to?(:read_attribute)
raw_value = record.read_attribute(attr_name)
end
else
before_type_cast = :"#{attr_name}_before_type_cast"
if record.respond_to?(before_type_cast)
raw_value = record.public_send(before_type_cast)
end
end
raw_value || value
end
After reading it roughly, it seems that if there is an implementation of # {attribute} _before_type_cast
, that value will be used.
Since ActiveModel
does not include the functions around before_type_cast
, it is validated with the value after casting.
If ActiveModel
has the function of before_type_cast
, it will be the same validation as ActiveRecord
.
I searched for a source that implements the functionality of before_type_cast
in ActiveRecord
https://github.com/rails/rails/blob/2a0d4950398d1c5b5cd73186ac5269a9afa933e8/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
Include
this module in the form class
hoge_form.rb
class HogeForm
include ActiveModel::Model
include ActiveModel::Attributes
include ActiveModel::AttributeMethods
#add to
include ActiveRecord::AttributeMethods::BeforeTypeCast
attribute :number, :integer
validates :number, numericality: { only_integer: true }
end
Operation check]
form = HogeForm.new
form.number = 'hoge'
form.number #Value after cast
=> 0
form.number_before_type_cast # before_type_cast is available
=> "hoge"
form.valid?
=> false #invalid state
form.errors.messages
=> {:number=>["Please enter a numerical value"]} #Not a number(hoge)So error
form.number = 1.5 #Substitute decimal
=> 1.5
form.number #cast 1
=> 1
form.valid? # invalid
=> false
form.errors.messages
=> {:number=>["Please enter as an integer"]} #1 before the cast properly.Validated against 5
I think it is better to incorporate BeforeTypeCast into ActiveModel as it should be.
I didn't seem to have any side effects when I include`` ActiveRecord :: AttributeMethods :: BeforeTypeCast
.
It seems that it is not recommended (I do not take responsibility for the defect)
Our Collabbit is looking for a Rails engineer https://github.com/collabit-inc/job-offer-engineer