Rails Value Objects – One Validator to Rule Them All
Tags: ruby, oop, value, validate
Published: 2019-01-23 14:16:00

---
title: Rails Value Objects – One Validator to Rule Them All
published: true
description: Use a single small ActiveModel Validator class for all of your value objects
tags: ruby,oop,value,validate
series: Getting the Most Out of Ruby Value Objects
---

# TL;DR

Validation of all value objects can be achieved with a small custom Validator class.

# Problem

When you define a value object in Ruby, and in particular in Rails, it is a natural part of its functionality that it should tell the difference between valid and invalid initialisation inputs.

So a value object would naturally have a `#valid?` method, and maybe an `#errors` method as well.

Since Rails gives you the ability to apply validations to attributes, do you also need one custom validator for each value object? Is there a succinct and efficient way of connecting Rails validators and your value objects?

# Background

A value object can generally tell us whether it is valid or not. Indeed this might be a major reason for creating a value object, so that it can encapsulate all of the validation logic, and respond to `valid?` appropriately. `@email.valid?`, `@zip_code.valid?`, `@isbn.valid?`, etc.

For example consider a Country object that is initialised with a two-letter ISO country code such as "GB" or "FR". It can reference a valid codes list to define the list of valid country codes, and various attributes: its name, whether it currently exists or not, perhaps a capital city name.

So when input is accepted we need to check that the input value is valid according to the code list (and maybe we add something to clean up any easily corrected issues like lower case inputs or spaces).

Therefore we might fairly expect that `Country.new("XA").valid?` will respond with `false`, and `Country.new("x1").errors` should respond with `["Country codes must be a two-letter ISO 3166-1 alpha-2 code"]`. And you're correct, I do write the best and most precise error messages in the world.

> In many cases you will have sanitised the input in front-end logic of course, and for a code from a small list of values or for a simple algorithmic validation, that is always going to help. For more complex cases (is the ISBN in a range that we recognise to have been assigned by ISBN International, for example) we may defer the logic to the server, and rely on a value object for validation anyway.

We probably want to protect our database from invalid values, by using standard Rails validation in the model class:

```ruby
  validates :country, erm what goes here?
```

# Solution: The Valid Validator

If all of our value objects define the same responses, then the way is open to treat them the same way  with a single custom validator.

Here is a class that we can use to validate any object that can enumerate its errors:

```ruby
class ValidValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    value.errors.each { |error| record.errors.add(attribute, error) }
  end
end

```

I think that we can all agree that that is an imaginative and accurate name, although I would also accept `NoErrorsValidator`.

All of the errors read from the value object are added to the record errors, and every value object that implements an `errors` method can be validated with this same class.

So here's how you use it:
```ruby
  validates :country, valid: true
```
... with all of the usual options ...

```ruby
  validates :country, valid: true, allow_nil: true
```
`allow_nil: true` causes the validation to be skipped if the value object responds to `nil?` with `true`, so make sure your value objects respond to that – see also [Ruby nil value objects](https://dev.to/databasesponge/ruby-nil-value-objects-3bno) in this series.

And here we have it – one ActiveModel validator to rule them all.

```ruby
  validates :country,             valid: true, allow_nil: true
  validates :construction_method, valid: true, allow_nil: false
  validates :height,              valid: true, allow_nil: false
  validates :occupancy_class,     valid: true, allow_nil: false   
```

If by "all" we mean "value objects", that is.