mutations icon indicating copy to clipboard operation
mutations copied to clipboard

Feature request: `either_of` filter

Open MOZGIII opened this issue 4 years ago • 3 comments

I need an either_of filter. It should accept a hash of values where exactly one key of the listed if filled:

either_of do
  hash :a do
    string :f1
  end
  hash :b do
    string :f2
    string :f3
  end
  hash :c do
    string :f4
    string :f5
  end
end

Valid:

  • {"a": { "f1": "qwe" }}
  • {"b": { "f2": "qwe", "f3": "wqe" }}
  • {"c": { "f4": "asd", "f5": "asd }}

Error:

  • {}
  • {"a": { "f1": "qwe" }, "b": { "f2": "qwe", "f3": "wqe" }}
  • { "d": {} }
  • { "a": { "f1": "qwe" }, "d": "qwe" }

What do you think?

MOZGIII avatar Dec 12 '19 17:12 MOZGIII

This seems like it would be a lot of work to implement, in order to satisfy a very specific use case.

It would be much easier, if a little uglier, to achieve this as a custom validation instead:

def validate
  unless (raw_inputs.keys & %w(a b c)).one?
    add_error(...)
  end
end

If you did still want to work on this feature, I think I'd prefer to see a combination of two options:

any_of { ... }, which uses the first filter that matches the input strict: true, which tells a hash filter to treat unexpected inputs as an error

I'm interested in any_of because it could also be useful for arrays:

array :strings_and_numbers do
  any_of do
    string
    integer
  end
end

eugeneius avatar Dec 29 '19 00:12 eugeneius

I ended up with the following:

hash :value do
  optional do
    hash :a do
      string :f1
    end
    hash :b do
      string :f2
      string :f3
    end
    hash :c do
      string :f4
      string :f5
    end
  end
end

And a validator:

add_error(...) unless value.count == 1

And an accessor:

discriminant, data = value.first

It does the trick

I model algebraic enum, so it doesn't make sense to allow specifying nov value directly (as that would not provide the discriminant, which is mandatory). Instead, to allow nil I'd do something like this:

optional do
  hash :value do
    optional do
      string :a
      integer :b
    end
  end
end

I'd still like to have an either_of, though a bit different now.

either_of :value do
  hash :a do
    string :f1
  end
  hash :b do
    string :f2
    string :f3
  end
  hash :c do
    string :f4
    string :f5
  end
end

Notice how there's :value specified now.

If should also be usable with arrays:

array :strings_and_numbers do
  either_of do
    string :a
    integer :b
  end
end
array :strings_and_numbers do
  optional do
    either_of do
      string :a
      integer :b
    end
  end
end

MOZGIII avatar Dec 29 '19 01:12 MOZGIII

Seems like I'll be using these quite a lot, and I'm a bit tired of using custom validators.

MOZGIII avatar Dec 29 '19 01:12 MOZGIII