Theory: Validations performed on data outside of request

This isn’t specifically a question about the behaviour of dry-validation, but rather a question of whether dry-validation fits this use-case or whether something else is more appropriate.

Part of our company’s workflow allows a user to create records with relaxed validations in an inactive state. A more strict set of validations is then applied when the user attempts to activate the record.
For example, the creation flow might contain something like:

class CreateProductContract < ApplicationContract
  params do
    optional(:name).value(:string)
    # ... omitted for brevity ...
  end
end

Whereas the activation flow would have constraints such that:

class ActivateProductContract < ApplicationContract
  params do
    required(:name).filled(:string)
    # ... omitted for brevity ...
  end
end

The gotcha here is that the name attribute isn’t part of the activation request. The frontend sends only an id to an activation endpoint, and data is loaded from the record for the action.
To my understanding, to use dry-validation for this, the record and its associations would need to be decomposed into a Hash to be run through the contract. Basically, something that does:

def map_catalog_item_to_product(catalog_item)
  {
    name:     catalog_item.title,
    variants: map_offering_sizes_to_variants(catalog_item.offering.sizes),
    # ... omitted for brevity ...
  }
end

I’m hesitant to take this approach as the validated data structure is quite deep, and encapsulates quite a few nested data objects with a good 50 fields between them. This would be quite the time sink, both in terms of maintenance time and run time.

Currently we leverage Rails to perform model validations, where some validations are being run inside of an on: :activation scope. Roughly:

class CatalogItem < ApplicationRecord # Referred to as `Product` in public API
  # ...
  validates :title, presence: true, on: :activation # Referred to as `name` in public API
  # ...
end

This works but also creates another big problem, because the frontend relies on the error keys being consistent with the keys in the request payloads.
Errors produced by ActiveRecord match the data structure of our database rather than the data structure of our API, forcing us to perform a lot of flakey transformations and mapping. (ie. title vs. name above.)

TL;DR:

  • Is dry-validation capable of / intended for validating data outside of a request?
  • Is there a better library or better approach to this class of problem?
  • If building out a Hash is a viable approach, is there a way to do it more cleanly? I’ve looked at Transproc, but that looks to be intended for transforming scalar types and doesn’t play well with ActiveRecord models.

Is dry-validation capable of / intended for validating data outside of a request?

Completely. dry-validation is a generic data validation library. It provides help to specialize when the input is coming from request parameters, json, etc, but it doesn’t need to. Just use plain schema schema if you don’t need any type of coercion:

https://dry-rb.org/gems/dry-validation/1.0/schemas/#defining-a-schema-without-coercion

Is there a better library or better approach to this class of problem?

If you don’t need business rules validation and just care about shape, you can go with dry-schema. dry-validation is just another layer on top of it.

If building out a Hash is a viable approach, is there a way to do it more cleanly? I’ve looked at Transproc , but that looks to be intended for transforming scalar types and doesn’t play well with ActiveRecord models.

I guess here you have some manual work to do. Ideally, if your project is not too big or you have time you could change ActiveRecord for [rom]( https://rom-rb.org. It would help you with those mapping between names.