Validate multiple contexts on valid? and invalid? at once#21069
Conversation
Example:
```ruby
class Person
include ActiveModel::Validations
attr_reader :name, :title
validates_presence_of :name, on: :create
validates_presence_of :title, on: :update
end
person = Person.new
person.valid?([:create, :update]) # => true
person.errors.messages # => {:name=>["can't be blank"], :title=>["can't be blank"]}
```
d794ed4 to
86e3b04
Compare
|
Hi @dmitry. I couldn't realise the real world example. Can you give a code example to show as a developer in which case I need to check the validity of an object in multiple contexts. |
|
@meinac hey, thank you for the critics! For example, imagine you have a multiple steps before you could create an order. You would like to add more and more validations on each steps, so each step will validate all the previous step validations + current one. Another example, actually what I've got recently (but we are still on rails 3.2.x, hopefully will update soon or later), when you want to validate model for almost everything, except presence of the user, in that case when user isn't logged in yet or not created his profile, which will be created through the nested attributes. I can think of other possible cases, which can be solved a other way around, but in some cases it's really better to have this feature to make everything easier. |
|
👍 This would enable to remove conditional validations. Much needed feature. |
|
Through it's 5x slower, it's really helpful feature. require 'benchmark'
a = [:a, :b]
b = :b
n = 10000000
Benchmark.bm do |x|
x.report { n.times do !(Array(a) & Array(b)).empty? end }
x.report { n.times do Array(a).include?(b) end }
end |
|
I would be glad to rebase this PR if anyone from the core agrees to merge it into the master. |
|
How much does I don't think this change is worth it over just doing |
|
@kaspth I've answered on the both questions above :) With Slowdown is 5x times, but can be improved a little bit with |
|
Duh, sorry! 😄
Is that for |
|
Not x.report { n.times do !(Array(a) & Array(b)).empty? end }
x.report { n.times do Array(a).include?(b) end }I believe it will slowdown PS. Looks like it's much easier to close this PR, because such cases might be moved to the |
|
I don't think this slowdown will change anything. This feature is something that I wanted to implement some times so I think it is worth. |
There was a problem hiding this comment.
How many changes would be necessary to make person.valid?(:create, :update) work?
There was a problem hiding this comment.
Not many changes should be made, but the interface of the valid?(context) should be changed to valid?(*context) in activemodel/activerecord implementations (and everywhere where is valid? is used should be changed as well, like save, invalid? and so on). It can brake some gems.
In the beginning I went by this path, but then after changing a lot of places in the code, I found that using an array actually can fit better to whole conception, because it makes sense to pass an array instead of passing an array through a splat; passing a simple array without splat will look more clean in many cases.
…xts-at-once Validate multiple contexts on `valid?` and `invalid?` at once
|
It broke Active Record tests. I revert it. Could you take a look on these failures and open a new PR? |
|
@rafaelfranca fixed at #21535 PS. Broken because some code left when I tried to make it to work as splat params. |
- Followup of rails#21069. - [ci skip]
…e contexts.` Though the validation have supported multiple context since rails#21069, its callbacks don't support multiple context currently. So I regarded this as the bug and fixed. Example: ```ruby class Dog include ActiveModel::Validations include ActiveModel::Validations::Callbacks attr_accessor :history def initialize @history = [] end before_validation :set_before_validation_on_a, on: :a before_validation :set_before_validation_on_b, on: :b after_validation :set_after_validation_on_a, on: :a after_validation :set_after_validation_on_b, on: :b def set_before_validation_on_a; history << "before_validation on a"; end def set_before_validation_on_b; history << "before_validation on b"; end def set_after_validation_on_a; history << "after_validation on a" ; end def set_after_validation_on_b; history << "after_validation on b" ; end end ``` Before: ``` d = Dog.new d.valid?([:a, :b]) d.history # [] ``` After: ``` d = Dog.new d.valid?([:a, :b]) d.history # ["before_validation on a", "before_validation on b", "after_validation on a", "after_validation on b"] ```
Lets say we have cascade of validations, when we should check more and more validations, after each step. As an example:
precreatecontext should check everything in a create, but less 2 attributes (real word example).Currently it's only possible to check validations one by one. This patch provides possibility to do all the checks at once.
Example: