I have custom attribute setter in a Rails model in which i'm adding validation errors. However when record attributes are being updated 'true' returns as result, which kinda confusing to me. Any hints how to use validation errors inside custom setter?
Model:
class Post < ActiveRecord::Base
attr_accessible :body, :hidden_attribute, :title
def hidden_attribute=(value)
self.errors.add(:base, "not accepted")
self.errors.add(:hidden_attribute, "not_accepted")
write_attribute :hidden_attribute, value unless errors.any?
end
end
Console Output:
1.9.3p194 :024 > Post.last
Post Load (0.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
=> #<Post id: 1, title: "asdsaD", body: "la", hidden_attribute: nil, created_at: "2013-11-13 16:55:44", updated_at: "2013-11-13 16:56:06">
1.9.3p194 :025 > Post.last.update_attribute :hidden_attribute, "ka"
Post Load (0.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
(0.0ms) begin transaction
(0.0ms) commit transaction
=> true
I've made an example application for this case.
Okay, i understood the core of the issue. It's not possible to do what i want to achieve, because all the validations errors cleared out as soon as validation process starts.
https://github.com/rails/rails/blob/75b985e4e8b3319a4640a8d566d2f3eedce7918e/activemodel/lib/active_model/validations.rb#L178.
Custom setter kicks in way too early :(
In your setters, you can store the error messages in a temporary hash. Then you can create an ActiveRecord validation method to check if this temporary hash is empty and copy the error messages to errors
.
For example,
def age=(age)
raise ArgumentError unless age.is_a? Integer
self.age = age
rescue ArgumentError
@setter_errors ||= {}
@setter_errors[:age] ||= []
@setter_errors[:age] << 'invalid input'
end
Here's the ActiveRecord validation
validate :validate_no_setter_errors
def validate_no_setter_errors
@setter_errors.each do |attribute, messages|
messages.each do |message|
errors.add(attribute, message)
end
end
@setter_errors.empty?
end
To see this in action:
[2] pry(main)> p.age = 'old'
=> "old"
[3] pry(main)> p.save!
(1.0ms) BEGIN
(1.2ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Age invalid input
[4] pry(main)> p.errors.details
=> {:age=>[{:error=>"invalid input"}]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With