Created
August 9, 2012 15:52
-
-
Save coreyhaines/3305349 to your computer and use it in GitHub Desktop.
Thoughts on ActiveModel::Validator as a validation service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I love the helpers, they help to make the intention clear. | |
# I want to be able to do this. That is, using the validation helpers inside a validator object, | |
# rather than having to do manual validation. | |
class EmployeeValidator < ActiveModel::Validator | |
def validate(record) | |
record.validates_presence_of :email | |
end | |
end | |
# After some reading, I realize I can do this | |
class EmployeeValidator < ActiveModel::Validator | |
def setup(employee_class) | |
user_class.validates_presence_of :email | |
end | |
def validate(record) | |
end | |
end | |
# This provides the ability to use the helpers from a validator object. | |
# Now, I'm working on a way to bypass the validations from examples | |
# that aren't concerned with the validity of an object, such scope examples. | |
# Unfortunately, the setup method is called once when validates_with is called | |
# since ActiveRecord caches the validation objects. So, it has already been called | |
# and configured with the validation before a non-validation-oriented example | |
# can use it. So, no over-riding. | |
# Looking at the way validation helpers work, they all are backed by a subclass of | |
# Validator (or EachValidator), so you should be able to instantiate one and use it. | |
class EmployeeValidator < ActiveModel::Validator | |
def validate(record) | |
ActiveModel::Validations::PresenceValidator.new(attributes: [:first_name]).validate record | |
end | |
end | |
# This works for doing the validation. | |
# Yes, this is ugly, but we can wrap it in a helper, perhaps even having Active Record models | |
# expose the helpers on an instance level. | |
# However, when our example runs that wants to bypass them, it keeps using the old version. | |
# I want to be able to do this at the top of my validation-less example. | |
class EmployeeValidator < ActiveModel::Validator | |
def validate(record); end | |
end | |
# But, because the instance of the validator is cached when validates_with EmployeeValidator is | |
# called, my over-ride does not cut in. Boo! | |
# ActiveModel::Validations uses hooks to call the validations. | |
# Is there a way to disable the hook for a single example? | |
@codesoda Yeah, it does feel like an anti-pattern to have different validations based on different contexts of use. In production, you want the validations to run, it is just in the example (e.g. scope specs) that I don't want them to run.
@coreyhaines I knew if I creeped up in your gists I'd find something like this. Did you figure out how to disable in a test context?
I like the helpers as well but am thinking a PORO approach may be easier. Inject a NilValidator in some kind of test context. Currently working on this so I'll post whatever I come up with.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What do you think about the idea that one of a number of User validation classes could be used in different contexts.
I'm starting to think it could be an anti-pattern. If there are different validation rules based on the context, then the User model should probably be broken up.