activemodel 7.0.4 → 6.1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -103
- data/MIT-LICENSE +0 -1
- data/README.rdoc +3 -3
- data/lib/active_model/attribute.rb +0 -4
- data/lib/active_model/attribute_methods.rb +82 -67
- data/lib/active_model/attribute_set/builder.rb +10 -1
- data/lib/active_model/attribute_set.rb +1 -4
- data/lib/active_model/attributes.rb +12 -15
- data/lib/active_model/callbacks.rb +3 -3
- data/lib/active_model/conversion.rb +2 -2
- data/lib/active_model/dirty.rb +4 -5
- data/lib/active_model/error.rb +2 -2
- data/lib/active_model/errors.rb +248 -55
- data/lib/active_model/gem_version.rb +5 -5
- data/lib/active_model/locale/en.yml +0 -1
- data/lib/active_model/model.rb +59 -6
- data/lib/active_model/naming.rb +8 -15
- data/lib/active_model/secure_password.rb +2 -25
- data/lib/active_model/serialization.rb +2 -6
- data/lib/active_model/translation.rb +2 -2
- data/lib/active_model/type/date.rb +1 -1
- data/lib/active_model/type/helpers/numeric.rb +1 -9
- data/lib/active_model/type/helpers/time_value.rb +3 -3
- data/lib/active_model/type/integer.rb +1 -4
- data/lib/active_model/type/registry.rb +38 -8
- data/lib/active_model/type/time.rb +1 -1
- data/lib/active_model/type.rb +6 -6
- data/lib/active_model/validations/absence.rb +2 -2
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/callbacks.rb +1 -1
- data/lib/active_model/validations/clusivity.rb +1 -1
- data/lib/active_model/validations/confirmation.rb +5 -5
- data/lib/active_model/validations/exclusion.rb +3 -3
- data/lib/active_model/validations/format.rb +1 -1
- data/lib/active_model/validations/inclusion.rb +3 -3
- data/lib/active_model/validations/length.rb +2 -2
- data/lib/active_model/validations/numericality.rb +22 -29
- data/lib/active_model/validations/presence.rb +1 -1
- data/lib/active_model/validations/validates.rb +3 -3
- data/lib/active_model/validations/with.rb +4 -4
- data/lib/active_model/validations.rb +12 -12
- data/lib/active_model/validator.rb +5 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +0 -1
- metadata +9 -12
- data/lib/active_model/api.rb +0 -99
- data/lib/active_model/validations/comparability.rb +0 -29
- data/lib/active_model/validations/comparison.rb +0 -82
    
        data/lib/active_model/naming.rb
    CHANGED
    
    | @@ -3,7 +3,6 @@ | |
| 3 3 | 
             
            require "active_support/core_ext/hash/except"
         | 
| 4 4 | 
             
            require "active_support/core_ext/module/introspection"
         | 
| 5 5 | 
             
            require "active_support/core_ext/module/redefine_method"
         | 
| 6 | 
            -
            require "active_support/core_ext/module/delegation"
         | 
| 7 6 |  | 
| 8 7 | 
             
            module ActiveModel
         | 
| 9 8 | 
             
              class Name
         | 
| @@ -154,7 +153,6 @@ module ActiveModel | |
| 154 153 | 
             
                # Returns a new ActiveModel::Name instance. By default, the +namespace+
         | 
| 155 154 | 
             
                # and +name+ option will take the namespace and name of the given class
         | 
| 156 155 | 
             
                # respectively.
         | 
| 157 | 
            -
                # Use +locale+ argument for singularize and pluralize model name.
         | 
| 158 156 | 
             
                #
         | 
| 159 157 | 
             
                #   module Foo
         | 
| 160 158 | 
             
                #     class Bar
         | 
| @@ -163,7 +161,7 @@ module ActiveModel | |
| 163 161 | 
             
                #
         | 
| 164 162 | 
             
                #   ActiveModel::Name.new(Foo::Bar).to_s
         | 
| 165 163 | 
             
                #   # => "Foo::Bar"
         | 
| 166 | 
            -
                def initialize(klass, namespace = nil, name = nil | 
| 164 | 
            +
                def initialize(klass, namespace = nil, name = nil)
         | 
| 167 165 | 
             
                  @name = name || klass.name
         | 
| 168 166 |  | 
| 169 167 | 
             
                  raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
         | 
| @@ -171,17 +169,16 @@ module ActiveModel | |
| 171 169 | 
             
                  @unnamespaced = @name.delete_prefix("#{namespace.name}::") if namespace
         | 
| 172 170 | 
             
                  @klass        = klass
         | 
| 173 171 | 
             
                  @singular     = _singularize(@name)
         | 
| 174 | 
            -
                  @plural       = ActiveSupport::Inflector.pluralize(@singular | 
| 175 | 
            -
                  @uncountable  = @plural == @singular
         | 
| 172 | 
            +
                  @plural       = ActiveSupport::Inflector.pluralize(@singular)
         | 
| 176 173 | 
             
                  @element      = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
         | 
| 177 174 | 
             
                  @human        = ActiveSupport::Inflector.humanize(@element)
         | 
| 178 175 | 
             
                  @collection   = ActiveSupport::Inflector.tableize(@name)
         | 
| 179 176 | 
             
                  @param_key    = (namespace ? _singularize(@unnamespaced) : @singular)
         | 
| 180 177 | 
             
                  @i18n_key     = @name.underscore.to_sym
         | 
| 181 178 |  | 
| 182 | 
            -
                  @route_key          = (namespace ? ActiveSupport::Inflector.pluralize(@param_key | 
| 183 | 
            -
                  @singular_route_key = ActiveSupport::Inflector.singularize(@route_key | 
| 184 | 
            -
                  @route_key << "_index" if @ | 
| 179 | 
            +
                  @route_key          = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
         | 
| 180 | 
            +
                  @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
         | 
| 181 | 
            +
                  @route_key << "_index" if @plural == @singular
         | 
| 185 182 | 
             
                end
         | 
| 186 183 |  | 
| 187 184 | 
             
                # Transform the model name into a more human format, using I18n. By default,
         | 
| @@ -209,10 +206,6 @@ module ActiveModel | |
| 209 206 | 
             
                  I18n.translate(defaults.shift, **options)
         | 
| 210 207 | 
             
                end
         | 
| 211 208 |  | 
| 212 | 
            -
                def uncountable?
         | 
| 213 | 
            -
                  @uncountable
         | 
| 214 | 
            -
                end
         | 
| 215 | 
            -
             | 
| 216 209 | 
             
                private
         | 
| 217 210 | 
             
                  def _singularize(string)
         | 
| 218 211 | 
             
                    ActiveSupport::Inflector.underscore(string).tr("/", "_")
         | 
| @@ -239,7 +232,7 @@ module ActiveModel | |
| 239 232 | 
             
              # is required to pass the \Active \Model Lint test. So either extending the
         | 
| 240 233 | 
             
              # provided method below, or rolling your own is required.
         | 
| 241 234 | 
             
              module Naming
         | 
| 242 | 
            -
                def self.extended(base)  | 
| 235 | 
            +
                def self.extended(base) #:nodoc:
         | 
| 243 236 | 
             
                  base.silence_redefinition_of_method :model_name
         | 
| 244 237 | 
             
                  base.delegate :model_name, to: :class
         | 
| 245 238 | 
             
                end
         | 
| @@ -286,7 +279,7 @@ module ActiveModel | |
| 286 279 | 
             
                #   ActiveModel::Naming.uncountable?(Sheep) # => true
         | 
| 287 280 | 
             
                #   ActiveModel::Naming.uncountable?(Post)  # => false
         | 
| 288 281 | 
             
                def self.uncountable?(record_or_class)
         | 
| 289 | 
            -
                   | 
| 282 | 
            +
                  plural(record_or_class) == singular(record_or_class)
         | 
| 290 283 | 
             
                end
         | 
| 291 284 |  | 
| 292 285 | 
             
                # Returns string to use while generating route names. It differs for
         | 
| @@ -328,7 +321,7 @@ module ActiveModel | |
| 328 321 | 
             
                  model_name_from_record_or_class(record_or_class).param_key
         | 
| 329 322 | 
             
                end
         | 
| 330 323 |  | 
| 331 | 
            -
                def self.model_name_from_record_or_class(record_or_class)  | 
| 324 | 
            +
                def self.model_name_from_record_or_class(record_or_class) #:nodoc:
         | 
| 332 325 | 
             
                  if record_or_class.respond_to?(:to_model)
         | 
| 333 326 | 
             
                    record_or_class.to_model.model_name
         | 
| 334 327 | 
             
                  else
         | 
| @@ -36,9 +36,7 @@ module ActiveModel | |
| 36 36 | 
             
                  #
         | 
| 37 37 | 
             
                  #   gem 'bcrypt', '~> 3.1.7'
         | 
| 38 38 | 
             
                  #
         | 
| 39 | 
            -
                  #  | 
| 40 | 
            -
                  #
         | 
| 41 | 
            -
                  # ===== Using Active Record (which automatically includes ActiveModel::SecurePassword)
         | 
| 39 | 
            +
                  # Example using Active Record (which automatically includes ActiveModel::SecurePassword):
         | 
| 42 40 | 
             
                  #
         | 
| 43 41 | 
             
                  #   # Schema: User(name:string, password_digest:string, recovery_password_digest:string)
         | 
| 44 42 | 
             
                  #   class User < ActiveRecord::Base
         | 
| @@ -60,27 +58,6 @@ module ActiveModel | |
| 60 58 | 
             
                  #   user.authenticate_recovery_password('42password')          # => user
         | 
| 61 59 | 
             
                  #   User.find_by(name: 'david')&.authenticate('notright')      # => false
         | 
| 62 60 | 
             
                  #   User.find_by(name: 'david')&.authenticate('mUc3m00RsqyRe') # => user
         | 
| 63 | 
            -
                  #
         | 
| 64 | 
            -
                  # ===== Conditionally requiring a password
         | 
| 65 | 
            -
                  #
         | 
| 66 | 
            -
                  #   class Account
         | 
| 67 | 
            -
                  #     include ActiveModel::SecurePassword
         | 
| 68 | 
            -
                  #
         | 
| 69 | 
            -
                  #     attr_accessor :is_guest, :password_digest
         | 
| 70 | 
            -
                  #
         | 
| 71 | 
            -
                  #     has_secure_password
         | 
| 72 | 
            -
                  #
         | 
| 73 | 
            -
                  #     def errors
         | 
| 74 | 
            -
                  #       super.tap { |errors| errors.delete(:password, :blank) if is_guest }
         | 
| 75 | 
            -
                  #     end
         | 
| 76 | 
            -
                  #   end
         | 
| 77 | 
            -
                  #
         | 
| 78 | 
            -
                  #   account = Account.new
         | 
| 79 | 
            -
                  #   account.valid? # => false, password required
         | 
| 80 | 
            -
                  #
         | 
| 81 | 
            -
                  #   account.is_guest = true
         | 
| 82 | 
            -
                  #   account.valid? # => true
         | 
| 83 | 
            -
                  #
         | 
| 84 61 | 
             
                  def has_secure_password(attribute = :password, validations: true)
         | 
| 85 62 | 
             
                    # Load bcrypt gem only when has_secure_password is used.
         | 
| 86 63 | 
             
                    # This is to avoid ActiveModel (and by extension the entire framework)
         | 
| @@ -142,7 +119,7 @@ module ActiveModel | |
| 142 119 | 
             
                    #   user.authenticate_password('mUc3m00RsqyRe') # => user
         | 
| 143 120 | 
             
                    define_method("authenticate_#{attribute}") do |unencrypted_password|
         | 
| 144 121 | 
             
                      attribute_digest = public_send("#{attribute}_digest")
         | 
| 145 | 
            -
                       | 
| 122 | 
            +
                      BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
         | 
| 146 123 | 
             
                    end
         | 
| 147 124 |  | 
| 148 125 | 
             
                    alias_method :authenticate, :authenticate_password if attribute == :password
         | 
| @@ -123,7 +123,7 @@ module ActiveModel | |
| 123 123 | 
             
                #   user.serializable_hash(include: { notes: { only: 'title' }})
         | 
| 124 124 | 
             
                #   # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
         | 
| 125 125 | 
             
                def serializable_hash(options = nil)
         | 
| 126 | 
            -
                  attribute_names =  | 
| 126 | 
            +
                  attribute_names = attributes.keys
         | 
| 127 127 |  | 
| 128 128 | 
             
                  return serializable_attributes(attribute_names) if options.blank?
         | 
| 129 129 |  | 
| @@ -149,10 +149,6 @@ module ActiveModel | |
| 149 149 | 
             
                end
         | 
| 150 150 |  | 
| 151 151 | 
             
                private
         | 
| 152 | 
            -
                  def attribute_names_for_serialization
         | 
| 153 | 
            -
                    attributes.keys
         | 
| 154 | 
            -
                  end
         | 
| 155 | 
            -
             | 
| 156 152 | 
             
                  # Hook method defining how an attribute value should be retrieved for
         | 
| 157 153 | 
             
                  # serialization. By default this is assumed to be an instance named after
         | 
| 158 154 | 
             
                  # the attribute. Override this method in subclasses should you need to
         | 
| @@ -181,7 +177,7 @@ module ActiveModel | |
| 181 177 | 
             
                  #   +association+ - name of the association
         | 
| 182 178 | 
             
                  #   +records+     - the association record(s) to be serialized
         | 
| 183 179 | 
             
                  #   +opts+        - options for the association records
         | 
| 184 | 
            -
                  def serializable_add_includes(options = {})  | 
| 180 | 
            +
                  def serializable_add_includes(options = {}) #:nodoc:
         | 
| 185 181 | 
             
                    return unless includes = options[:include]
         | 
| 186 182 |  | 
| 187 183 | 
             
                    unless includes.is_a?(Hash)
         | 
| @@ -17,12 +17,12 @@ module ActiveModel | |
| 17 17 | 
             
              #
         | 
| 18 18 | 
             
              # This also provides the required class methods for hooking into the
         | 
| 19 19 | 
             
              # Rails internationalization API, including being able to define a
         | 
| 20 | 
            -
              # class | 
| 20 | 
            +
              # class based +i18n_scope+ and +lookup_ancestors+ to find translations in
         | 
| 21 21 | 
             
              # parent classes.
         | 
| 22 22 | 
             
              module Translation
         | 
| 23 23 | 
             
                include ActiveModel::Naming
         | 
| 24 24 |  | 
| 25 | 
            -
                # Returns the +i18n_scope+ for the class.  | 
| 25 | 
            +
                # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
         | 
| 26 26 | 
             
                def i18n_scope
         | 
| 27 27 | 
             
                  :activemodel
         | 
| 28 28 | 
             
                end
         | 
| @@ -25,18 +25,10 @@ module ActiveModel | |
| 25 25 | 
             
                    end
         | 
| 26 26 |  | 
| 27 27 | 
             
                    def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc:
         | 
| 28 | 
            -
                       | 
| 29 | 
            -
                        !equal_nan?(old_value, new_value_before_type_cast)
         | 
| 28 | 
            +
                      super || number_to_non_number?(old_value, new_value_before_type_cast)
         | 
| 30 29 | 
             
                    end
         | 
| 31 30 |  | 
| 32 31 | 
             
                    private
         | 
| 33 | 
            -
                      def equal_nan?(old_value, new_value)
         | 
| 34 | 
            -
                        (old_value.is_a?(::Float) || old_value.is_a?(BigDecimal)) &&
         | 
| 35 | 
            -
                          old_value.nan? &&
         | 
| 36 | 
            -
                          old_value.instance_of?(new_value.class) &&
         | 
| 37 | 
            -
                          new_value.nan?
         | 
| 38 | 
            -
                      end
         | 
| 39 | 
            -
             | 
| 40 32 | 
             
                      def number_to_non_number?(old_value, new_value_before_type_cast)
         | 
| 41 33 | 
             
                        old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
         | 
| 42 34 | 
             
                      end
         | 
| @@ -12,9 +12,9 @@ module ActiveModel | |
| 12 12 |  | 
| 13 13 | 
             
                      if value.acts_like?(:time)
         | 
| 14 14 | 
             
                        if is_utc?
         | 
| 15 | 
            -
                          value = value.getutc if !value.utc?
         | 
| 15 | 
            +
                          value = value.getutc if value.respond_to?(:getutc) && !value.utc?
         | 
| 16 16 | 
             
                        else
         | 
| 17 | 
            -
                          value = value.getlocal
         | 
| 17 | 
            +
                          value = value.getlocal if value.respond_to?(:getlocal)
         | 
| 18 18 | 
             
                        end
         | 
| 19 19 | 
             
                      end
         | 
| 20 20 |  | 
| @@ -36,7 +36,7 @@ module ActiveModel | |
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 38 | 
             
                    def type_cast_for_schema(value)
         | 
| 39 | 
            -
                      value. | 
| 39 | 
            +
                      value.to_s(:db).inspect
         | 
| 40 40 | 
             
                    end
         | 
| 41 41 |  | 
| 42 42 | 
             
                    def user_input_in_time_zone(value)
         | 
| @@ -1,38 +1,68 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module ActiveModel
         | 
| 4 | 
            +
              # :stopdoc:
         | 
| 4 5 | 
             
              module Type
         | 
| 5 | 
            -
                class Registry | 
| 6 | 
            +
                class Registry
         | 
| 6 7 | 
             
                  def initialize
         | 
| 7 | 
            -
                    @registrations =  | 
| 8 | 
            +
                    @registrations = []
         | 
| 8 9 | 
             
                  end
         | 
| 9 10 |  | 
| 10 | 
            -
                  def  | 
| 11 | 
            +
                  def initialize_dup(other)
         | 
| 11 12 | 
             
                    @registrations = @registrations.dup
         | 
| 12 13 | 
             
                    super
         | 
| 13 14 | 
             
                  end
         | 
| 14 15 |  | 
| 15 | 
            -
                  def register(type_name, klass = nil, &block)
         | 
| 16 | 
            +
                  def register(type_name, klass = nil, **options, &block)
         | 
| 16 17 | 
             
                    unless block_given?
         | 
| 17 18 | 
             
                      block = proc { |_, *args| klass.new(*args) }
         | 
| 18 19 | 
             
                      block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
         | 
| 19 20 | 
             
                    end
         | 
| 20 | 
            -
                    registrations | 
| 21 | 
            +
                    registrations << registration_klass.new(type_name, block, **options)
         | 
| 21 22 | 
             
                  end
         | 
| 22 23 |  | 
| 23 24 | 
             
                  def lookup(symbol, *args)
         | 
| 24 | 
            -
                    registration =  | 
| 25 | 
            +
                    registration = find_registration(symbol, *args)
         | 
| 25 26 |  | 
| 26 27 | 
             
                    if registration
         | 
| 27 | 
            -
                      registration.call(symbol, *args)
         | 
| 28 | 
            +
                      registration.call(self, symbol, *args)
         | 
| 28 29 | 
             
                    else
         | 
| 29 30 | 
             
                      raise ArgumentError, "Unknown type #{symbol.inspect}"
         | 
| 30 31 | 
             
                    end
         | 
| 31 32 | 
             
                  end
         | 
| 32 | 
            -
                  ruby2_keywords(:lookup)
         | 
| 33 | 
            +
                  ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
         | 
| 33 34 |  | 
| 34 35 | 
             
                  private
         | 
| 35 36 | 
             
                    attr_reader :registrations
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def registration_klass
         | 
| 39 | 
            +
                      Registration
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    def find_registration(symbol, *args, **kwargs)
         | 
| 43 | 
            +
                      registrations.find { |r| r.matches?(symbol, *args, **kwargs) }
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                class Registration
         | 
| 48 | 
            +
                  # Options must be taken because of https://bugs.ruby-lang.org/issues/10856
         | 
| 49 | 
            +
                  def initialize(name, block, **)
         | 
| 50 | 
            +
                    @name = name
         | 
| 51 | 
            +
                    @block = block
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def call(_registry, *args)
         | 
| 55 | 
            +
                    block.call(*args)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                  ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def matches?(type_name, *args, **kwargs)
         | 
| 60 | 
            +
                    type_name == name
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  private
         | 
| 64 | 
            +
                    attr_reader :name, :block
         | 
| 36 65 | 
             
                end
         | 
| 37 66 | 
             
              end
         | 
| 67 | 
            +
              # :startdoc:
         | 
| 38 68 | 
             
            end
         | 
| @@ -33,7 +33,7 @@ module ActiveModel | |
| 33 33 | 
             
                      return apply_seconds_precision(value) unless value.is_a?(::String)
         | 
| 34 34 | 
             
                      return if value.empty?
         | 
| 35 35 |  | 
| 36 | 
            -
                      dummy_time_value = value.sub(/\A\d | 
| 36 | 
            +
                      dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "2000-01-01 ")
         | 
| 37 37 |  | 
| 38 38 | 
             
                      fast_string_to_time(dummy_time_value) || begin
         | 
| 39 39 | 
             
                        time_hash = ::Date._parse(dummy_time_value)
         | 
    
        data/lib/active_model/type.rb
    CHANGED
    
    | @@ -24,15 +24,15 @@ module ActiveModel | |
| 24 24 | 
             
                class << self
         | 
| 25 25 | 
             
                  attr_accessor :registry # :nodoc:
         | 
| 26 26 |  | 
| 27 | 
            -
                  # Add a new type to the registry, allowing it to be  | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
                    registry.register(type_name, klass, &block)
         | 
| 27 | 
            +
                  # Add a new type to the registry, allowing it to be gotten through ActiveModel::Type#lookup
         | 
| 28 | 
            +
                  def register(type_name, klass = nil, **options, &block)
         | 
| 29 | 
            +
                    registry.register(type_name, klass, **options, &block)
         | 
| 31 30 | 
             
                  end
         | 
| 32 31 |  | 
| 33 | 
            -
                  def lookup( | 
| 34 | 
            -
                    registry.lookup( | 
| 32 | 
            +
                  def lookup(*args) # :nodoc:
         | 
| 33 | 
            +
                    registry.lookup(*args)
         | 
| 35 34 | 
             
                  end
         | 
| 35 | 
            +
                  ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
         | 
| 36 36 |  | 
| 37 37 | 
             
                  def default_value # :nodoc:
         | 
| 38 38 | 
             
                    @default_value ||= Value.new
         | 
| @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            module ActiveModel
         | 
| 4 4 | 
             
              module Validations
         | 
| 5 5 | 
             
                # == \Active \Model Absence Validator
         | 
| 6 | 
            -
                class AbsenceValidator < EachValidator  | 
| 6 | 
            +
                class AbsenceValidator < EachValidator #:nodoc:
         | 
| 7 7 | 
             
                  def validate_each(record, attr_name, value)
         | 
| 8 8 | 
             
                    record.errors.add(attr_name, :present, **options) if value.present?
         | 
| 9 9 | 
             
                  end
         | 
| @@ -24,7 +24,7 @@ module ActiveModel | |
| 24 24 | 
             
                  #
         | 
| 25 25 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 26 26 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 27 | 
            -
                  # See ActiveModel::Validations | 
| 27 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 28 28 | 
             
                  def validates_absence_of(*attr_names)
         | 
| 29 29 | 
             
                    validates_with AbsenceValidator, _merge_attributes(attr_names)
         | 
| 30 30 | 
             
                  end
         | 
| @@ -104,7 +104,7 @@ module ActiveModel | |
| 104 104 | 
             
                  #
         | 
| 105 105 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 106 106 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 107 | 
            -
                  # See ActiveModel::Validations | 
| 107 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information.
         | 
| 108 108 | 
             
                  def validates_acceptance_of(*attr_names)
         | 
| 109 109 | 
             
                    validates_with AcceptanceValidator, _merge_attributes(attr_names)
         | 
| 110 110 | 
             
                  end
         | 
| @@ -4,7 +4,7 @@ require "active_support/core_ext/range" | |
| 4 4 |  | 
| 5 5 | 
             
            module ActiveModel
         | 
| 6 6 | 
             
              module Validations
         | 
| 7 | 
            -
                module Clusivity  | 
| 7 | 
            +
                module Clusivity #:nodoc:
         | 
| 8 8 | 
             
                  ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
         | 
| 9 9 | 
             
                                  "and must be supplied as the :in (or :within) option of the configuration hash"
         | 
| 10 10 |  | 
| @@ -19,13 +19,13 @@ module ActiveModel | |
| 19 19 |  | 
| 20 20 | 
             
                  private
         | 
| 21 21 | 
             
                    def setup!(klass)
         | 
| 22 | 
            -
                      klass.attr_reader(*attributes. | 
| 22 | 
            +
                      klass.attr_reader(*attributes.map do |attribute|
         | 
| 23 23 | 
             
                        :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
         | 
| 24 | 
            -
                      end)
         | 
| 24 | 
            +
                      end.compact)
         | 
| 25 25 |  | 
| 26 | 
            -
                      klass.attr_writer(*attributes. | 
| 26 | 
            +
                      klass.attr_writer(*attributes.map do |attribute|
         | 
| 27 27 | 
             
                        :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
         | 
| 28 | 
            -
                      end)
         | 
| 28 | 
            +
                      end.compact)
         | 
| 29 29 | 
             
                    end
         | 
| 30 30 |  | 
| 31 31 | 
             
                    def confirmation_value_equal?(record, attribute, value, confirmed)
         | 
| @@ -71,7 +71,7 @@ module ActiveModel | |
| 71 71 | 
             
                  #
         | 
| 72 72 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 73 73 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 74 | 
            -
                  # See ActiveModel::Validations | 
| 74 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 75 75 | 
             
                  def validates_confirmation_of(*attr_names)
         | 
| 76 76 | 
             
                    validates_with ConfirmationValidator, _merge_attributes(attr_names)
         | 
| 77 77 | 
             
                  end
         | 
| @@ -29,8 +29,8 @@ module ActiveModel | |
| 29 29 | 
             
                  #
         | 
| 30 30 | 
             
                  # Configuration options:
         | 
| 31 31 | 
             
                  # * <tt>:in</tt> - An enumerable object of items that the value shouldn't
         | 
| 32 | 
            -
                  #   be part of. This can be supplied as a proc, lambda | 
| 33 | 
            -
                  #   enumerable. If the enumerable is a numerical, time | 
| 32 | 
            +
                  #   be part of. This can be supplied as a proc, lambda or symbol which returns an
         | 
| 33 | 
            +
                  #   enumerable. If the enumerable is a numerical, time or datetime range the test
         | 
| 34 34 | 
             
                  #   is performed with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When
         | 
| 35 35 | 
             
                  #   using a proc or lambda the instance under validation is passed as an argument.
         | 
| 36 36 | 
             
                  # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
         | 
| @@ -40,7 +40,7 @@ module ActiveModel | |
| 40 40 | 
             
                  #
         | 
| 41 41 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 42 42 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 43 | 
            -
                  # See ActiveModel::Validations | 
| 43 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 44 44 | 
             
                  def validates_exclusion_of(*attr_names)
         | 
| 45 45 | 
             
                    validates_with ExclusionValidator, _merge_attributes(attr_names)
         | 
| 46 46 | 
             
                  end
         | 
| @@ -104,7 +104,7 @@ module ActiveModel | |
| 104 104 | 
             
                  #
         | 
| 105 105 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 106 106 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 107 | 
            -
                  # See ActiveModel::Validations | 
| 107 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 108 108 | 
             
                  def validates_format_of(*attr_names)
         | 
| 109 109 | 
             
                    validates_with FormatValidator, _merge_attributes(attr_names)
         | 
| 110 110 | 
             
                  end
         | 
| @@ -28,8 +28,8 @@ module ActiveModel | |
| 28 28 | 
             
                  #
         | 
| 29 29 | 
             
                  # Configuration options:
         | 
| 30 30 | 
             
                  # * <tt>:in</tt> - An enumerable object of available items. This can be
         | 
| 31 | 
            -
                  #   supplied as a proc, lambda | 
| 32 | 
            -
                  #   enumerable is a numerical, time | 
| 31 | 
            +
                  #   supplied as a proc, lambda or symbol which returns an enumerable. If the
         | 
| 32 | 
            +
                  #   enumerable is a numerical, time or datetime range the test is performed
         | 
| 33 33 | 
             
                  #   with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When using
         | 
| 34 34 | 
             
                  #   a proc or lambda the instance under validation is passed as an argument.
         | 
| 35 35 | 
             
                  # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
         | 
| @@ -38,7 +38,7 @@ module ActiveModel | |
| 38 38 | 
             
                  #
         | 
| 39 39 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 40 40 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 41 | 
            -
                  # See ActiveModel::Validations | 
| 41 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 42 42 | 
             
                  def validates_inclusion_of(*attr_names)
         | 
| 43 43 | 
             
                    validates_with InclusionValidator, _merge_attributes(attr_names)
         | 
| 44 44 | 
             
                  end
         | 
| @@ -117,8 +117,8 @@ module ActiveModel | |
| 117 117 | 
             
                  #   <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
         | 
| 118 118 | 
             
                  #
         | 
| 119 119 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 120 | 
            -
                  # +:if+, +:unless+, +:on | 
| 121 | 
            -
                  # See ActiveModel::Validations | 
| 120 | 
            +
                  # +:if+, +:unless+, +:on+ and +:strict+.
         | 
| 121 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 122 122 | 
             
                  def validates_length_of(*attr_names)
         | 
| 123 123 | 
             
                    validates_with LengthValidator, _merge_attributes(attr_names)
         | 
| 124 124 | 
             
                  end
         | 
| @@ -1,34 +1,27 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "active_model/validations/comparability"
         | 
| 4 3 | 
             
            require "bigdecimal/util"
         | 
| 5 4 |  | 
| 6 5 | 
             
            module ActiveModel
         | 
| 7 6 | 
             
              module Validations
         | 
| 8 7 | 
             
                class NumericalityValidator < EachValidator # :nodoc:
         | 
| 9 | 
            -
                   | 
| 8 | 
            +
                  CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
         | 
| 9 | 
            +
                             equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
         | 
| 10 | 
            +
                             odd: :odd?, even: :even?, other_than: :!= }.freeze
         | 
| 10 11 |  | 
| 11 | 
            -
                   | 
| 12 | 
            -
                  NUMBER_CHECKS = { odd: :odd?, even: :even? }
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  RESERVED_OPTIONS = COMPARE_CHECKS.keys + NUMBER_CHECKS.keys + RANGE_CHECKS.keys + [:only_integer]
         | 
| 12 | 
            +
                  RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
         | 
| 15 13 |  | 
| 16 14 | 
             
                  INTEGER_REGEX = /\A[+-]?\d+\z/
         | 
| 17 15 |  | 
| 18 16 | 
             
                  HEXADECIMAL_REGEX = /\A[+-]?0[xX]/
         | 
| 19 17 |  | 
| 20 18 | 
             
                  def check_validity!
         | 
| 21 | 
            -
                     | 
| 19 | 
            +
                    keys = CHECKS.keys - [:odd, :even]
         | 
| 20 | 
            +
                    options.slice(*keys).each do |option, value|
         | 
| 22 21 | 
             
                      unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
         | 
| 23 22 | 
             
                        raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
         | 
| 24 23 | 
             
                      end
         | 
| 25 24 | 
             
                    end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    options.slice(*RANGE_CHECKS.keys).each do |option, value|
         | 
| 28 | 
            -
                      unless value.is_a?(Range)
         | 
| 29 | 
            -
                        raise ArgumentError, ":#{option} must be a range"
         | 
| 30 | 
            -
                      end
         | 
| 31 | 
            -
                    end
         | 
| 32 25 | 
             
                  end
         | 
| 33 26 |  | 
| 34 27 | 
             
                  def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
         | 
| @@ -44,18 +37,23 @@ module ActiveModel | |
| 44 37 |  | 
| 45 38 | 
             
                    value = parse_as_number(value, precision, scale)
         | 
| 46 39 |  | 
| 47 | 
            -
                    options.slice(* | 
| 48 | 
            -
                       | 
| 49 | 
            -
             | 
| 40 | 
            +
                    options.slice(*CHECKS.keys).each do |option, option_value|
         | 
| 41 | 
            +
                      case option
         | 
| 42 | 
            +
                      when :odd, :even
         | 
| 43 | 
            +
                        unless value.to_i.public_send(CHECKS[option])
         | 
| 50 44 | 
             
                          record.errors.add(attr_name, option, **filtered_options(value))
         | 
| 51 45 | 
             
                        end
         | 
| 52 | 
            -
                       | 
| 53 | 
            -
                         | 
| 54 | 
            -
             | 
| 46 | 
            +
                      else
         | 
| 47 | 
            +
                        case option_value
         | 
| 48 | 
            +
                        when Proc
         | 
| 49 | 
            +
                          option_value = option_value.call(record)
         | 
| 50 | 
            +
                        when Symbol
         | 
| 51 | 
            +
                          option_value = record.send(option_value)
         | 
| 55 52 | 
             
                        end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                        option_value =  | 
| 58 | 
            -
             | 
| 53 | 
            +
             | 
| 54 | 
            +
                        option_value = parse_as_number(option_value, precision, scale)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                        unless value.public_send(CHECKS[option], option_value)
         | 
| 59 57 | 
             
                          record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
         | 
| 60 58 | 
             
                        end
         | 
| 61 59 | 
             
                      end
         | 
| @@ -63,10 +61,6 @@ module ActiveModel | |
| 63 61 | 
             
                  end
         | 
| 64 62 |  | 
| 65 63 | 
             
                private
         | 
| 66 | 
            -
                  def option_as_number(record, option_value, precision, scale)
         | 
| 67 | 
            -
                    parse_as_number(option_value(record, option_value), precision, scale)
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 64 | 
             
                  def parse_as_number(raw_value, precision, scale)
         | 
| 71 65 | 
             
                    if raw_value.is_a?(Float)
         | 
| 72 66 | 
             
                      parse_float(raw_value, precision, scale)
         | 
| @@ -161,7 +155,7 @@ module ActiveModel | |
| 161 155 | 
             
                  # Configuration options:
         | 
| 162 156 | 
             
                  # * <tt>:message</tt> - A custom error message (default is: "is not a number").
         | 
| 163 157 | 
             
                  # * <tt>:only_integer</tt> - Specifies whether the value has to be an
         | 
| 164 | 
            -
                  #   integer (default is +false+).
         | 
| 158 | 
            +
                  #   integer, e.g. an integral value (default is +false+).
         | 
| 165 159 | 
             
                  # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
         | 
| 166 160 | 
             
                  #   +false+). Notice that for Integer and Float columns empty strings are
         | 
| 167 161 | 
             
                  #   converted to +nil+.
         | 
| @@ -179,11 +173,10 @@ module ActiveModel | |
| 179 173 | 
             
                  #   supplied value.
         | 
| 180 174 | 
             
                  # * <tt>:odd</tt> - Specifies the value must be an odd number.
         | 
| 181 175 | 
             
                  # * <tt>:even</tt> - Specifies the value must be an even number.
         | 
| 182 | 
            -
                  # * <tt>:in</tt> - Check that the value is within a range.
         | 
| 183 176 | 
             
                  #
         | 
| 184 177 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 185 178 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
         | 
| 186 | 
            -
                  # See ActiveModel::Validations | 
| 179 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 187 180 | 
             
                  #
         | 
| 188 181 | 
             
                  # The following checks can also be supplied with a proc or a symbol which
         | 
| 189 182 | 
             
                  # corresponds to a method:
         | 
| @@ -30,7 +30,7 @@ module ActiveModel | |
| 30 30 | 
             
                  #
         | 
| 31 31 | 
             
                  # There is also a list of default options supported by every validator:
         | 
| 32 32 | 
             
                  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
         | 
| 33 | 
            -
                  # See ActiveModel::Validations | 
| 33 | 
            +
                  # See <tt>ActiveModel::Validations#validates</tt> for more information
         | 
| 34 34 | 
             
                  def validates_presence_of(*attr_names)
         | 
| 35 35 | 
             
                    validates_with PresenceValidator, _merge_attributes(attr_names)
         | 
| 36 36 | 
             
                  end
         | 
| @@ -78,14 +78,14 @@ module ActiveModel | |
| 78 78 | 
             
                  #   or an array of symbols. (e.g. <tt>on: :create</tt> or
         | 
| 79 79 | 
             
                  #   <tt>on: :custom_validation_context</tt> or
         | 
| 80 80 | 
             
                  #   <tt>on: [:create, :custom_validation_context]</tt>)
         | 
| 81 | 
            -
                  # * <tt>:if</tt> - Specifies a method, proc | 
| 81 | 
            +
                  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
         | 
| 82 82 | 
             
                  #   if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
         | 
| 83 83 | 
             
                  #   or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
         | 
| 84 84 | 
             
                  #   proc or string should return or evaluate to a +true+ or +false+ value.
         | 
| 85 | 
            -
                  # * <tt>:unless</tt> - Specifies a method, proc | 
| 85 | 
            +
                  # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
         | 
| 86 86 | 
             
                  #   if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
         | 
| 87 87 | 
             
                  #   or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
         | 
| 88 | 
            -
                  #   method, proc | 
| 88 | 
            +
                  #   method, proc or string should return or evaluate to a +true+ or
         | 
| 89 89 | 
             
                  #   +false+ value.
         | 
| 90 90 | 
             
                  # * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
         | 
| 91 91 | 
             
                  # * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.
         | 
| @@ -51,16 +51,16 @@ module ActiveModel | |
| 51 51 | 
             
                  #   or an array of symbols. (e.g. <tt>on: :create</tt> or
         | 
| 52 52 | 
             
                  #   <tt>on: :custom_validation_context</tt> or
         | 
| 53 53 | 
             
                  #   <tt>on: [:create, :custom_validation_context]</tt>)
         | 
| 54 | 
            -
                  # * <tt>:if</tt> - Specifies a method, proc | 
| 54 | 
            +
                  # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
         | 
| 55 55 | 
             
                  #   if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
         | 
| 56 56 | 
             
                  #   or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
         | 
| 57 | 
            -
                  #   The method, proc | 
| 57 | 
            +
                  #   The method, proc or string should return or evaluate to a +true+ or
         | 
| 58 58 | 
             
                  #   +false+ value.
         | 
| 59 | 
            -
                  # * <tt>:unless</tt> - Specifies a method, proc | 
| 59 | 
            +
                  # * <tt>:unless</tt> - Specifies a method, proc or string to call to
         | 
| 60 60 | 
             
                  #   determine if the validation should not occur
         | 
| 61 61 | 
             
                  #   (e.g. <tt>unless: :skip_validation</tt>, or
         | 
| 62 62 | 
             
                  #   <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
         | 
| 63 | 
            -
                  #   The method, proc | 
| 63 | 
            +
                  #   The method, proc or string should return or evaluate to a +true+ or
         | 
| 64 64 | 
             
                  #   +false+ value.
         | 
| 65 65 | 
             
                  # * <tt>:strict</tt> - Specifies whether validation should be strict.
         | 
| 66 66 | 
             
                  #   See <tt>ActiveModel::Validations#validates!</tt> for more information.
         |