# \[Dry-Schema\] Adding rules to Dry::Schema

**URL:** <https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242>\
**Category:** Support\
**Created:** [April 15, 2021, 5:52am UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242 "2021-04-15T05:52:08Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattc](https://avatars.discourse-cdn.com/v4/letter/m/85f322/32.png) [@mattc](https://discourse.dry-rb.org/u/mattc)\
**Post date:** [April 15, 2021, 5:52am UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/1 "2021-04-15T05:52:08Z")

</div>

Hi, I’m currently migrating from `dry-validation-0.13` to `dry-schema-1.6.1` and couldn’t find a way to add validation rules.  
Here’s an example I had when using Dry::Validation

```ruby
Dry::Validation.Params do
  ...
  rule(role_for_manage_kind: %i[filter_by_kind filter_by_role]) do |filter_by_kind, filter_by_role|
    filter_by_kind.eql?('manage').then(filter_by_role.filled?)
  end
end

```

This is basically a conditional validation, if the value of **filter\_by\_kind** is equal ‘manage’, the field **filter\_by\_role** should be required  
Any idea of how I go about this but using **“Dry::Schema”** instead?

Thanks in advance

---

<div class="post-metadata">

**Author:** ![mattc](https://avatars.discourse-cdn.com/v4/letter/m/85f322/32.png) [@mattc](https://discourse.dry-rb.org/u/mattc)\
**Post date:** [April 16, 2021, 1:14am UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/2 "2021-04-16T01:14:25Z")

</div>

@solnic Would you know if this is possible?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [April 16, 2021, 8:57am UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/3 "2021-04-16T08:57:18Z")

</div>

Why are you migrating to dry-schema and not dry-validation 1.x? Rules are supported by dry-validation, there’s no such concept in dry-schema.

---

<div class="post-metadata">

**Author:** ![Arnaud](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arnaud/32/663_2.png) [@Arnaud](https://discourse.dry-rb.org/u/Arnaud)\
**Post date:** [June 22, 2021, 4:26pm UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/5 "2021-06-22T16:26:02Z")

</div>

This is somewhat related.

In a pre-1.0 dry-validation version, I had a `Dry::Validation.Schema` setup which allowed the use of custom methods to validate some fields. I think this can be replicated with rules in `Dry::Validation::Contract`, but previously we could also reuse/embed the schema around.

Somehow, taking from email examples, I’d like to have something like this:

```auto
class ExampleAddress < Dry::Validation::Contract do
  schema do
    required(:name).filled(:string)
    required(:email).filled(:string)
  end

  rule(:email) do 
    # ... custom validation
  end
end

class ExampleForm < Dry::Validation::Contract do
  schema do
    required(:message)
    required(:to).hash(ExampleAddress)
    required(:from).hash(ExampleAddress)
  end
end

```

Of course, that does not work. I’ve navigated the docs of dry-validation and dry-schema but I’ve been unable to find an answer. 😅

Can this be done at all with the 1.6 version, or did the feature disappear?

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [June 22, 2021, 6:58pm UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/6 "2021-06-22T18:58:40Z")

</div>

Currently, Contracts don’t support composition. There is [an open issue](https://github.com/dry-rb/dry-validation/issues/593) with an unfinished implementation of this feature, but this is still a work in progress.

Schemas may be composed, though, so I would recommend splitting this up along those lines. You may [share rules as macros](https://dry-rb.org/gems/dry-validation/1.6/macros/) in 1.6, although this is considered an experimental feature so maybe you don’t want to rely on it.

```ruby
Dry::Validation.register_macro(:email_format) do
  unless /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value)
    key.failure('not a valid email format')
  end
end

EmailAddress = Dry::Schema.Params do
  required(:name).filled
  required(:email).filled
end

class ExampleForm < Dry::Validation::Contract
  schema do
    required(:message)
    required(:to).schema(EmailAddress)
    required(:from).schema(EmailAddress)
  end

  rule(to: :email).validate(:email_format)
  rule(from: :email).validate(:email_format)
end

```

---

<div class="post-metadata">

**Author:** ![Arnaud](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/arnaud/32/663_2.png) [@Arnaud](https://discourse.dry-rb.org/u/Arnaud)\
**Post date:** [June 23, 2021, 2:42pm UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/7 "2021-06-23T14:42:27Z")

</div>

This could work, thanks!

BTW the [website’s docs](https://dry-rb.org/gems/dry-validation/1.6/macros/#using-a-macro-with-rules) have a different syntax for this, which fails to compile (`validate` isn’t defined for the class). I know how docs VS code can be, just thought I’d mention it. 🙂

The macro-based approach looks messy to be honest. After playing with it, I know that it won’t work in my particular case because my Form class validates an array of messages, so the macro block gets a `nil` value in that case. I’m not sure if that’s a bug of just that I’m abusing a feature in a new way. 🙂

```auto
class MoreForm < Dry::Validation::Contract
  register_macro(:email_macro) do
    # value => nil
  end

  schema do
    required(:data).array(:hash) do
      required(:to).schema(AddressSchema)
      required(:from).schema(AddressSchema)
      # ...
    end
  end

  rule(to: :email).validate(:email_macro)
end

```

* * *

Reading the docs, I get the sense that you’re encouraged to build a hierarchy of contracts classes, where macros and configuration are inherited. Wouldn’t that be simpler to have plain old ruby methods instead of passing around code blocks in that case?

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [June 23, 2021, 6:15pm UTC](https://discourse.dry-rb.org/t/dry-schema-adding-rules-to-dry-schema/1242/8 "2021-06-23T18:15:57Z")

</div>

I would recommend having at least an `ApplicationContract` with your default configuration and macros, yes
