aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/validations/callbacks.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/validations/callbacks.rb')
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb122
1 files changed, 122 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
new file mode 100644
index 0000000000..887d31ae2a
--- /dev/null
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+module ActiveModel
+ module Validations
+ # == Active \Model \Validation \Callbacks
+ #
+ # Provides an interface for any class to have +before_validation+ and
+ # +after_validation+ callbacks.
+ #
+ # First, include ActiveModel::Validations::Callbacks from the class you are
+ # creating:
+ #
+ # class MyModel
+ # include ActiveModel::Validations::Callbacks
+ #
+ # before_validation :do_stuff_before_validation
+ # after_validation :do_stuff_after_validation
+ # end
+ #
+ # Like other <tt>before_*</tt> callbacks if +before_validation+ throws
+ # +:abort+ then <tt>valid?</tt> will not be called.
+ module Callbacks
+ extend ActiveSupport::Concern
+
+ included do
+ include ActiveSupport::Callbacks
+ define_callbacks :validation,
+ skip_after_callbacks_if_terminated: true,
+ scope: [:kind, :name]
+ end
+
+ module ClassMethods
+ # Defines a callback that will get called right before validation.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ # include ActiveModel::Validations::Callbacks
+ #
+ # attr_accessor :name
+ #
+ # validates_length_of :name, maximum: 6
+ #
+ # before_validation :remove_whitespaces
+ #
+ # private
+ #
+ # def remove_whitespaces
+ # name.strip!
+ # end
+ # end
+ #
+ # person = Person.new
+ # person.name = ' bob '
+ # person.valid? # => true
+ # person.name # => "bob"
+ def before_validation(*args, &block)
+ options = args.extract_options!
+
+ if options.key?(:on)
+ options = options.dup
+ options[:on] = Array(options[:on])
+ options[:if] = Array(options[:if])
+ options[:if].unshift ->(o) {
+ !(options[:on] & Array(o.validation_context)).empty?
+ }
+ end
+
+ set_callback(:validation, :before, *args, options, &block)
+ end
+
+ # Defines a callback that will get called right after validation.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ # include ActiveModel::Validations::Callbacks
+ #
+ # attr_accessor :name, :status
+ #
+ # validates_presence_of :name
+ #
+ # after_validation :set_status
+ #
+ # private
+ #
+ # def set_status
+ # self.status = errors.empty?
+ # end
+ # end
+ #
+ # person = Person.new
+ # person.name = ''
+ # person.valid? # => false
+ # person.status # => false
+ # person.name = 'bob'
+ # person.valid? # => true
+ # person.status # => true
+ def after_validation(*args, &block)
+ options = args.extract_options!
+ options = options.dup
+ options[:prepend] = true
+
+ if options.key?(:on)
+ options[:on] = Array(options[:on])
+ options[:if] = Array(options[:if])
+ options[:if].unshift ->(o) {
+ !(options[:on] & Array(o.validation_context)).empty?
+ }
+ end
+
+ set_callback(:validation, :after, *args, options, &block)
+ end
+ end
+
+ private
+
+ # Overwrite run validations to include callbacks.
+ def run_validations!
+ _run_validation_callbacks { super }
+ end
+ end
+ end
+end