These are chat archives for dry-rb/chat

9th
Aug 2017
Rafael George
@cored
Aug 09 2017 11:38
@prikha the approach that I followed before dry-transaction which I still trying to figure out a way to do TDD with it. It's just to return something that I call view models so every single define transaction will return a transformation to represent the processing of the set of operations without changing the type of my known entities; that been said I normally leave the serialization part of things to the outer layer of it. So I put that inside maybe a Rails controller without putting any 'view logic' inside them. They are mostly like data structures that represent a json structure.
Joshua Hansen
@binarypaladin
Aug 09 2017 19:04

I've been playing with dry-validations a lot lately and something that doesn't seem to be covered in the docs heavily is inheritance. A common scenario that I see is this: I have a "basic" schema that has a lot of optional/maybe fields but then I have stricter versions where that's not the case. See this example:

loose_schema = Dry::Validation.Schema do
  configure do
    config.type_specs = true

    def proper_description?(value)
      # logic goes here
    end
  end

  required(:label, :string).filled(:str?)
  optional(:description, :string).maybe(:str?, :proper_description?)
end

strict_schema = Dry::Validation.Schema do
  # How do I make :description required/filled here?
end

I would like to avoid having to repeat :str? and :proper_description? in the strict version.

Rafael George
@cored
Aug 09 2017 20:36
@binarypaladin I didn't know that dry-validation supported inheritance
Aaron Barthel
@abrthel
Aug 09 2017 20:36

@binarypaladin, I believe that duplicate rules are overwritten in descendant schemas so you could do something like this

strict_schema = Dry::Validation.Schema(loose_schema) do
  required(:description, :string).maybe(:str?, :proper_description?)
end

but you'd have to include the maybe.
https://github.com/dry-rb/dry-validation/blob/5baf161f7335577e3e9d7465706c6b11ca7fe4c0/lib/dry/validation/schema/class_interface.rb#L44

There might be some way to craft a custome rule which handles the shared logic but I don't have time to play around with it atm. Something like

base_schema = Dry::Validation.Schema do
  configure do
    config.type_specs = true

    def proper_description?(value)
      # logic goes here
    end
  end

  required(:label, :string).filled(:str?)
  optional(:description)

  rule(description_rule: [:description]) do |desc|
    desc.proper_description?.or(desc.str?)
  end
end
Rafael George
@cored
Aug 09 2017 20:37
I believe that this validation happen in context and since that's true there are two different constraints; why don't you just have a description with strictness validation in one schema and the softer one in the other ?
to me this is a better approach since you are not doing an specialization of code but sharing "similar" behavior between the two schemas
and since similar is not the same I would rather write two different rules
Joshua Hansen
@binarypaladin
Aug 09 2017 20:55
The issue I see is that if a field has like, 4 - 5 rules on it WHEN present and I want a schema where it MUST be present, that's a lot of duplication, not to mention if the rules change I have two places to update them. I've been messing around a bit and I can handle this with rules if I want. Sometimes other schemas make sense, but there seem to be instances where I want to toggle the behavior. After reading through the macros and understanding maybe better... this isn't such an issue. (Part me of wants to always use required.)
Joshua Hansen
@binarypaladin
Aug 09 2017 21:06

This leads me to another question in concern to rules:

Let's say a particular condition leads to multiple validations and I want them all to validate. In this example:

Entity = Dry::Validation.Schema do
  configure do
    config.type_specs = true

    def company?(value)
      !person?(value)
    end

    def entity_type?(value)
      %w[company person trust].include?(value)
    end

    def person?(value)
      value == 'person'
    end
  end

  optional(:contact_name, :string)
  optional(:date_of_birth, :date)
  optional(:executive_name, :string)
  required(:name, :string).filled(:str?)

  required(:type, :string)
    .filled(:str?, :entity_type?)
    .when(:company?) do
      value(:contact_name).str?
      value(:executive_name).str?
    end
    .when(:person?) { value(:date_of_birth).date? }
end

What I want is when someone has selected company as a type, they see error messages for contact_name and executive_name. Right now, my option is this:

required(:type, :string)
  .filled(:str?, :entity_type?)
  .when(:company?) { value(:contact_name).str? }
  .when(:company?) { value(:executive_name).str? }
  .when(:person?) { value(:date_of_birth).date? }

The issue is that it's not particularly clean in that company? is being evaluated multiple times.

required(:type, :string)
  .filled(:str?, :entity_type?)
  .when(:company?) { value(:contact_name).str? & value(:executive_name).str? }
  .when(:person?) { value(:date_of_birth).date? }

The problem with this is that if contact_name is blank, executive_name will never trigger an error. This works for actually validation, but if a form isn't filled out in a certain order and you want to show inline errors on the field... this is problematic. If executive_name appeared before contact_name in the UX, it could lead to confusion. Is there a better way to achieve this besides calling .when(:company?) multiple times?

Joshua Hansen
@binarypaladin
Aug 09 2017 21:11
@abrthel I actually didn't see your post. That's helpful.