Closed darkbaby123 closed 8 years ago
@darkbaby123 atm there is a PR #71 that allows users to define custom validator which can work around this issue.
I like your suggestion and open for the PR submission. The validator should take in a proc/lambda OR the symbol name of a private condition method
@darkbaby123 Hello and thanks for this suggestion. We won't to support conditionals for action callbacks, validators, because they increase the complexity both of the frameworks and applications.
If you use validations around use cases, and not in entities/models, you'll almost eliminate this need. Now, I don't know your specific use case, so please share with us, and we can find a solution for you of for the framework.
As @joneslee85 we'll offer soon a more flexible approach for validations, so it could be helpful for you. But my opinion on conditionals is :-1:
@jodosha I read your blog post before I submit this issue, and I'm not use validations in entities/model level in my recent project (all plain Ruby object + validation mixin, it's a Rails project). I admit that most of time I don't need the conditional validations, but they're still useful in some of my cases:
I have an API that responsible to connect some device (hardware) to current user, and I use a single API endpoint to handle all 3 connection types. It's like this:
{
conn_type: 'a/b/c',
quick_code: 'xxx', // should be present in type a
uuid: 'xxx', // should be present in type b
}
In this case I can use 2 different validators, but a conditional validation can make it simpler.
In my another project I have a "sign contract" page, the user need to choose a "sign via" radio button to select sign type, he should either enter:
The validation in this logic is depend on what "sign via" he choose. So I did something like this:
validates :signer_driver_lic, :signer_state, presence: true, if: :sign_via_driver_lic?
validates :signer_ssn, presence: true, if: :sign_via_ssn?
I think conditional validations are not used frequently (if so then it's wrong way to use validations), but in some cases it make sense. A simple "if" is better than an extra abstraction (e.g. a different validator object).
BTW maybe out of the topic, but in these days I'm also thinking about what is a good validation system. I checked Ruby/Node/PHP implementations and I really like Laravel's validation because of 2 reasons:
if: :some_condition?
@darkbaby123 I made a mistake by evaluating this feature. My apologize.
Now, we could think of a better design that uses composition rather than a DSL. I still believe that :if
/:unless
once introduced, will go soon out of hand.
I'm reopening this, and inviting other devs who asked for the same feature, to better explain their needs and discuss together.
@jodosha Thank you, you're really open minded :smile:
I understand the concern about complexity. And most of the times it can be modeled in a different way. But sometimes the design decision is not up to the developer. So i guess this might be useful.
@pascalbetz Thank you. In my understanding from chat conversation, you had a concrete use case. Is it correct? Would you love to bring into this thread? Thank you!
:+1: for rethinking validation DSL and reconsidering. I'm absolutely convinced the DSL introduced in Rails was never-ever a good idea. Validations can be very complex, a DSL is limited, trying to make it flexible by introducing tons of options is not the way to go. Which is actually a general issue with any DSL. It's interesting that @darkbaby123 mentioned Laravel's validation with validators and rules, coincidently I've been thinking about a very similar approach, with lightweight validations that work with "rules" (I wanted to call them predicates) that are composable.
Validation DSL is a dead end, especially when you start to introduce more and more options.
I love Jose Valim's quote from one of his elixir talks where he mentioned that he gave a talk in 2010 titled "DSL or NoDSL" and then he says "I can give you a short digest of this talk: "NoDSL". Brilliant and very true.
And I agree. We should have less DSLs, in most cases we shouldn't have them at all.
@jodosha I can not point out one single project. I just had the requirements of conditional validations in various projects.
@solnic i don't know Laravel's validation. Can you explain?
Spoiler
:+1: for predicates within validators
:-1: for attribute :bar, if :foo?
Laravel uses php's lambda
to build conditional/complex validations, some kind of:
attribute :driver_lic do
presence: true if age > 18
size: 14
type: Integer
end
Those predicates are composed within main attribute and does not rely on DSL's to build it.
@pascalbetz boils down to what @hlegius just wrote :)
I'll be experimenting with a validation library soon, I can share some API ideas. The only reason why I may introduce a DSL is to reduce boilerplate required for composing validations, but I'm strongly against AM::V-like validation DSL, it's convoluted, inflexible and introduces ambiguity in many places which is a nonsense in a validation world (see :allow_blank
or :presence
validation etc.)
@solnic
let me know when you can share something. You've got me interested:-)
@hlegius thanks.
@pascalbetz will do :) the work will be happening in dry-validation repo (current prototype will be replaced)
@solnic can you tell me the link for Elixir talk? I also checked how validation works in Phoenix & Ecto, but I think it's not the best choice to use the same thing in Ruby land. But I'm very interested in how Jose think about DSLs.
Share some of my thinking about validation rules and validators:
A validation rule just check if a value is valid and nothing more. It doesn't care how to get the value from an data object, or how to set the error type or error message. It can be a pure function without side effect, so it can be used anywhere (even used standalone).
A validator is something like a form object. It includes how to apply validation rules to a data object's attributes. It gets a data object in, validate it, and set errors object. The errors object is maintained by validator but not data object. A validator's validate process has side effect: set errors object.
I found this kind of thinkings are more appeared in Node.js land, maybe it's because it's super easy to construct a plain object in JavaScript world. I saw an NPM library called composable-validator, but I can not find it now. It has really interesting ideas about how to composite validators.
I think to bring a good validator library, we can get ideas from many other languages (JavaScript, Elixir, PHP, etc.). Because validation issue are very common in programming world, and on the other side, many Ruby's validator libraries are very like Rails ActiveModel validator. That means if you don't agree with Rails way, you don't like the rest either.
OK I'm getting close to wrap up the rewrite of dry-validation. You can check out its DSL right here. It's based on simple (and stateless!) predicate logic and rule composition. Error objects are serializable to an AST for further processing, so various error messages can be produced (this needs a bit more work, I literally started a couple of hours ago).
The huge difference is that you're composing validations from simple predicate rules, they obey predicate logic and it's very precise, you can say that I expect key :email to be present, assuming it is, its value must be a string and it must match this regex
. This is something that cannot be easily done with huge DSL with lots of options, as it gets hairy very quickly and it's hard to reason about it.
I'm trying to put huge emphasis on preciseness and simple composition. It's not a good idea to have ambiguity and implicit behavior in a validation library. Next thing is that validation objects receive input that they will validate, rather than making them state-full using the input and making assumptions about things like how a value is being extracted from input. This means that in dry-validation you'll be able to validate plain hashes, plain arrays or objects with attr readers, this includes scenarios where things are deeply nested too.
The DSL is only meant to precisely define expected input structure along with the validation logic. I'm planning more advanced features like defining validation "safe-points" where you'll be able to say that if rule A passed then do this
or a system for generating error messages where information "from the outside" is required to produce the full error message to the client (which is often needed).
I'm very happy with the progress so far, I believe it's a good direction, definitely better than heavy DSLs with lots of options (it's an anti-pattern in general, regardless of the type of a library).
EDIT: updated the link to the spec
Looks like the spec mentioned above has moved: https://github.com/dryrb/dry-validation/blob/feature/predicate-set/spec/integration/validation_spec.rb
Just checked the repo and the spec is moved to https://github.com/dryrb/dry-validation/blob/master/spec/integration/validation_spec.rb
@darkbaby123 We finally coded a solution for your problem.
Please have a look at: #102 - For that PR I used your examples to implement the high level rules feature. https://github.com/hanami/validations/blob/814233a3a45e3fefe12e3d2f28850e59504a1b76/test/rules_test.rb
I found there's no conditional validations like
validates :name, presence, if: :some_condition
. IMO it's used very frequently. Curious the reason it's not implemented in Lotus validations. Is it already in plan? Or there's other way you recommend more to do the same thing?