Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Level hooks for validations

#107

This patch adds a :level option to validations. When a validation fails, it adds its failure notice to the Errors object indicated by the level. At this time only the level :error is supported, i.e., failures are added to the object accessed through my_model.errors.

The idea is to provide minimal support for applications or plugins to add further validation levels as needed.

Reported by Michael Schuerig · May 4th, 2008 @ 12:02 PM

State: stale
Milestone: none
Assigned to: Michael Koziarski Michael Koziarski
Importance: none

Activity

  1. Josh Susser
    Josh Susser

    I think I'd find this useful, though please don't build your API around a meta send, it's not really unnecessary and adds overhead. It's just as easy to implement in a way that's backward compatible and without the send.

    def errors(level=nil) # in AR::Base
    
    record.errors.add_to_base("Something is wrong")
    record.errors(:warnings).add_to_base("Something is wrong")
    

    Or something like that. A more generic name for #errors like #validation_failures might make more sense, and make #errors call validation_failures with for backward compatibility.

    I have some code that could use this feature to clean up a multilevel validation scheme, though it's not edge-compatible yet. I might try to play around with this approach anyway. If I've got so many comments on your code I should probably answer it with a patch.

    May 4th, 2008 @ 08:01 PM

  2. Michael Koziarski
    Michael Koziarski
    • Assigned user set to Michael Koziarski

    errors(nil) certainly seems like a reasonable approach, do you have any thoughts michael?

    May 4th, 2008 @ 11:12 PM

  3. Adam Meehan
    Adam Meehan

    I think I would find this useful also.

    To DRY it up a little could you not put the level extraction into the validates_each block above the loop so you only need to call it in one place. i.e.

    {{{

    def validates_each(*attrs)

    options = attrs.extract_options!.symbolize_keys

    attrs = attrs.flatten

    level = validation_level(options)

    1. Declare the validation.

    send(validation_method(options[:on] || :save), options) do |record|

    attrs.each do |attr|

    value = record.send(attr)

    next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])

    yield record, attr, value

    end

    end

    end

    }}}

    If it was tied into save so that as well as false you could pass a symbol or hash so you could be specific as to which error level was to be ignored on save. e.g

    {{{

    save(false, :allow => :warnings)

    }}}

    May 4th, 2008 @ 11:43 PM

  4. Adam Meehan
    Adam Meehan

    Sorry wrong formatting.

    I think I would find this useful also.

    To DRY it up a little could you not put the level extraction into the validates_each block above the loop so you only need to call it in one place. i.e.

    def validates_each(*attrs)
      options = attrs.extract_options!.symbolize_keys
      attrs   = attrs.flatten
    
      level = validation_level(options)
    
      # Declare the validation.
      send(validation_method(options[:on] || :save), options) do |record|
        attrs.each do |attr|
          value = record.send(attr)
          next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
          yield record, attr, value
        end
      end
    end
    

    If it was tied into save so that as well as false you could pass a symbol or hash so you could be specific as to which error level was to be ignored on save. e.g

    save(false, :allow => :warnings)
    

    May 4th, 2008 @ 11:46 PM

  5. Michael Schuerig
    Michael Schuerig

    Josh, Koz: I'm fine with

    def errors(level = nil)
      level ||= :errors
      ...
    

    Adam: I understand your DRY intention, but not how it's going to work. level isn't even used inside the block.

    save with an option to allow validation failures up to a certain level sounds interesting and is something I didn't think of. There are two aspects that make me hesitate:

    1. The first boolean arg and the latter :allow option are not orthogonal. I think the first arg should be either true/false or a hash.
    2. The functionality could be added by a plugin and doesn't have to go into core.

    May 5th, 2008 @ 01:01 AM

  6. Adam Meehan
    Adam Meehan

    Ok, I didn't see any use of the level variable outside of the validates_each block in your patch, with the exception of 'validates_presence_of'.

    My example is incomplete as I left out level in the yield. But on seconds thoughts, it would break existing plugins. So perhaps not.

    1. Agreed, was just first thought. A hash or boolean might be clearer.

    2. It would be useful in some of my work, so a plugin might be on the cards.

    May 5th, 2008 @ 01:56 AM

  7. Adam Meehan
    Adam Meehan

    Actually a cleaner solution than my first is to just add

    :level => :errors

    in the default configuration hash for each validation method. No need for an extra method to extract and pluralize it. Then just use configuration[:level] where needed.

    May 5th, 2008 @ 02:19 AM

  8. Michael Schuerig
    Michael Schuerig

    Using :error instead of :errors to designate the level was deliberate. To me :level => :error, :level => :warning, sound more natural and more in line with logging levels. However, I'm easily convinced otherwise.

    May 5th, 2008 @ 07:18 AM

  9. Adam Meehan
    Adam Meehan

    Your right it does sound more natural. Perhaps just leave the level singular and when a plural symbol is passed to the errors method as per Josh's suggestion then you can singularize it.

    Anyway enough of my musings. Lets see what the other guys think.

    May 5th, 2008 @ 08:26 AM

  10. Pratik
    Pratik

    I'm very tempted to say "Plugin material".

    May 14th, 2008 @ 12:17 PM

  11. Michael Schuerig
    Michael Schuerig

    Pratik, unfortunately this can't be done in a plugin without copying almost all of the validations -- which would be subject to break with changes in Rails.

    The idea is precisely to add these hooks to make them usable by plugins. The proposed changes, in either version, are backward compatible and robust in the face of changes.

    May 14th, 2008 @ 12:34 PM

  12. josh
    josh

    Errr, I really don't like this either. How could we refactor validations so you could write a plugin easier?

    May 14th, 2008 @ 05:20 PM

  13. Michael Schuerig
    Michael Schuerig

    I have no better idea at the moment. Put abstractly, there needs to be a way to parameterize where a failed validation add its message.

    May 14th, 2008 @ 05:58 PM

  14. Pratik
    Pratik

    Removing "patch" keyword.

    Also, I think it's worth noting that very soon we'll be moving validation stuff to ActiveModel, which already has some API changes. Probably, you should try to email core list about this feature request and find out what everyone else thinks.

    Thanks.

    May 20th, 2008 @ 01:31 PM

  15. Michael Schuerig
    Michael Schuerig

    I already send a message to rails-core on 2008-05-03, even before writing the patch. Koz advised me to write a patch and so I did.

    Regarding ActiveModel: Yes, that would be the right place to put this, however, I have no idea what the current state of ActiveModel is or if anyone is working on it.

    May 20th, 2008 @ 01:45 PM

  16. josh
    josh
    • State changed from new to stale
    • Tag set to activerecord, enhancement

    August 20th, 2008 @ 05:16 PM