I'd like to add something, and I'll include some potentially useful code
so hopefully my two cents is 'paid for'.
You've given example use cases in which you need a record in the
database to be 'invalid', temporarily. Is it possible that your
validations not covering a particular (exceptional) case? For example,
suppose a field is required EXCEPT in a certain condition. One case
that comes to mind is when the record represents a document that a user
can save in an incomplete state, returning later to complete it. It
might be allowable for some fields to be nil when the document is
incomplete.
Using update_attribute to accommodate this seems, to me, to discourage a
full elaboration of validations. This is particularly troubling to me
for two reasons:
1) Rails takes a dim view of database constraints, so model probably
bears more responsibility for overall system integrity than it would in
other frameworks.
2) Rails (rightly) insists on MVC, yet methods like update_attribute
diminish one of the best things about the 'M' - the domain-driven
approach in which 'model' really means 'a model of the business/domain'.
I would prefer to see an option on Rails, perhaps
config.strict_validation, that could be used by those of us who want to
be able to rely on the model classes in this way.
So, if config.strict_validation == true, we could run the following on
startup:
# Adds update_attribute!, which incurs validation, and
# issue warnings for use of methods that either bypass or
# don't throw exceptions on validation failure.
module ActiveRecord
class Base
# Incurs validation.
def update_attribute!(name, value)
send(name.to_s + '=', value)
save!
end
def update_attribute_with_warning(name, value)
ActiveRecord::Base.validation_warning(:update_attribute)
update_attribute_without_warning(name, value)
end
alias_method_chain :update_attribute, :warning
private
# Produce a warning encourage update_attribute! use
# and listing the line from which it was called.
def self.validation_warning(method_name)
# Don't warn about calls from this file or active_record...
unless caller[1] =~ /activerecord/
msg=<<EOM
warning: The method '#{method_name}' bypasses validation. Consider
'#{method_name}!'.
#{method_name} was called from #{caller[1]}
EOM
warn msg
end
end
end
end
In my own project, I also warn on save, update_attributes and create.
Thanks,
Brian Hartin
brian.hartin@(The free email system by Google).com