Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for array items in rule paths #607

Open
solnic opened this issue Dec 5, 2019 · 9 comments
Open

Support for array items in rule paths #607

solnic opened this issue Dec 5, 2019 · 9 comments

Comments

@solnic
Copy link
Member

solnic commented Dec 5, 2019

Examples

require 'dry-validation'

class PropsContract < Dry::Validation::Contract
  json do
    required(:contacts).value(:array, min_size?: 1).each do
      hash do
        required(:name).filled(:string)
        required(:email).filled(:string)
        required(:phone).filled(:string)
      end
    end
  end

  rule("contacts[].email").each do
    key.failure("email not valid") unless value.include?('@')
  end
end

c = PropsContract.new

c.(
  contacts: [
    { name: 'Jane', email: '[email protected]', phone: '123' },
    { name: 'John', email: 'oops', phone: '123' }
  ]).errors.to_h.inspect
# {:email=>{1=>["email not valid"]}}

Resources

Refs #603

@skryukov
Copy link
Member

skryukov commented Dec 8, 2019

@solnic shouldn't the rule be without .each?

  rule("contacts[].email") do
    key.failure("email not valid") unless value.include?('@')
  end

  rule("contacts[].emails").each do
    key.failure("email not valid") unless value.include?('@')
  end

@solnic
Copy link
Member Author

solnic commented Dec 8, 2019

@skryukov I think it should be with each, because otherwise we'd have to infer each from the path anyway. I prefer to keep it explicit.

@skryukov
Copy link
Member

skryukov commented Dec 9, 2019

@solnic I see your point, but how we will differentiate, should we iterate over emails or use email as a target item for the rule? Maybe we should introduce another method to explicitly tell dry-v to find array items inside a rule?

@solnic
Copy link
Member Author

solnic commented Dec 9, 2019

@skryukov wait, I think I’m missing something - differentiate what?

@skryukov
Copy link
Member

skryukov commented Dec 9, 2019

@solnic I'll try to explain =)

For example, we have a schema like this:

require 'dry-validation'

class PropsContract < Dry::Validation::Contract
  json do
    required(:contacts).value(:array, min_size?: 1).each do
      hash do
        required(:name).filled(:string)
        required(:email).filled(:string)
        required(:phone).filled(:string)
        required(:additional_emails).each(:string)
      end
    end
  end

  rule("contacts[].email").each do
    key.failure("email not valid") unless value.include?('@')
  end

  rule("contacts[].additional_emails").each do
    key.failure("email not valid") unless value.include?('@')
  end
end

c = PropsContract.new

c.(
  contacts: [
    { name: 'Jane', email: '[email protected]', phone: '123', additional_emails: ['oops'] },
    { name: 'John', email: 'oops', phone: '123', additional_emails: ['[email protected]'] }
  ]).errors.to_h.inspect
# => {:contacts =>{0=>{:additional_emails =>{0=>["email not valid"]}}},1=>{:email=>["email not valid"]}}

So how should we know when a rule needs to be applied to the item itself (email) and when on elements of the item (additional_emails)? And what if I want to check something like this:

  rule("contacts[].additional_emails").each(index:) do # or maybe even indexes in this case? =)
    key.failure("additional emails contain primary email") if value.include?(values[:contacts][index][:email])
  end

@flash-gordon
Copy link
Member

One more thing, contacts[].values[] <- how would that work? :) It's probably better to reject such paths

@solnic
Copy link
Member Author

solnic commented Dec 10, 2019

I don't know. Please feel free to figure it out however you think is best 😄

@solnic solnic added this to the 1.5.0 milestone Dec 13, 2019
@solnic solnic removed this from the 1.5.0 milestone Mar 11, 2020
@TimoMoss
Copy link

TimoMoss commented Nov 26, 2020

Hello, I think my question suits this issue as well

I have a macro:

register_macro(:numeric_string?) do
        condition = /^(\d)+$/.match?(value)
        message = 'must be a numeric string'
        key(keys.flatten).failure(message) unless condition
end

and I have to check in the same file both a single case such as the following:

rule(%i[data attributes token]).validate(:numeric_string?)

and for a separate case also array of numeric strings:

rule(%i[data attributes atrray_of_strings]).each do |r|
  r.validate(:numeric_string?)
end

the last one does not work

is there a right way how to do that?
if not, might be it should be added to the next versions?

Many thanks in advance

@solnic
Copy link
Member Author

solnic commented Dec 8, 2020

@TimoMoss for now you need to handle it manually, well implement a nice DSL for it at some point. For now I'm adding this to 1.7.0 milestone.

@solnic solnic added this to the 1.7.0 milestone Dec 8, 2020
@solnic solnic removed this from the 1.7.0 milestone Feb 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants