-
Notifications
You must be signed in to change notification settings - Fork 5
Applicative Validation in Ruby
Don't return nil
when validation fails, and try to handle it later:
def validate(txt)
if txt == "bar" then txt else nil end
end
# Oops!
validate("foo") + validate("bar") + validate("baz")
# NoMethodError: undefined method `+' for nil:NilClass
Don't raise
an exception, short-circuiting other validations:
def validate(txt)
if txt == "bar" then txt else raise "Invalid: #{txt}" end
end
# Only gives FIRST error:
validate("foo") + validate("bar") + validate("baz")
# RuntimeError: Invalid: foo
Don't effect external state to record errors:
def errors
@errors ||= []
end
def validate(txt)
if txt == "bar" then txt else errors << "Invalid: #{txt}"; "" end
end
# More complicated to test, parallelize, or just reason about
validate("foo") + validate("bar") + validate("baz")
# => "bar"
errors
# => ["Invalid: foo", "Invalid: baz"]
validate("foo") + validate("bar") + validate("baz")
# => "bar"
errors
# => ["Invalid: foo", "Invalid: baz", "Invalid: foo", "Invalid: baz"]
Do return a Disjoint Union type such as Either (docs).
The result will be either a success Right
containing the validated data, or a failure Left
value containing information about the failure.
def validate(txt)
if txt == "bar" then Right(txt) else Left("Invalid: #{txt}") end
end
validate("bar")
# => Right("bar")
validate("foo")
# => Left("Invalid: foo")
validate("foo").lift_to_a + validate("bar").lift_to_a + validate("baz").lift_to_a
# => Left(["Invalid: foo", "Invalid: baz"])
Using applicative validation patterns, you can combine the results of your validations easily and with clarity. In the case of failure you can process all generated errors, and easily combine and transform the result in the case of success.
Examples:
-
Inspired by the blog post An example of applicative validation in FSharpx, and is a direct port of the Scala example.
-
Multiple blog comment validations
A shorter and perhaps more realistic example involving comment validation for a blog post.
TODO: Add links to more information here.