diff options
Diffstat (limited to 'activemodel/lib/active_model/validations')
4 files changed, 70 insertions, 10 deletions
| diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index 1bcfedb35d..c5c0cd4636 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -14,16 +14,63 @@ module ActiveModel        end        private +        def setup!(klass) -        attr_readers = attributes.reject { |name| klass.attribute_method?(name) } -        attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") } -        klass.send(:attr_reader, *attr_readers) -        klass.send(:attr_writer, *attr_writers) +        klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))        end        def acceptable_option?(value)          Array(options[:accept]).include?(value)        end + +      class LazilyDefineAttributes < Module +        def initialize(attribute_definition) +          define_method(:respond_to_missing?) do |method_name, include_private=false| +            super(method_name, include_private) || attribute_definition.matches?(method_name) +          end + +          define_method(:method_missing) do |method_name, *args, &block| +            if attribute_definition.matches?(method_name) +              attribute_definition.define_on(self.class) +              send(method_name, *args, &block) +            else +              super(method_name, *args, &block) +            end +          end +        end +      end + +      class AttributeDefinition +        def initialize(attributes) +          @attributes = attributes.map(&:to_s) +        end + +        def matches?(method_name) +          attr_name = convert_to_reader_name(method_name) +          attributes.include?(attr_name) +        end + +        def define_on(klass) +          attr_readers = attributes.reject { |name| klass.attribute_method?(name) } +          attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") } +          klass.send(:attr_reader, *attr_readers) +          klass.send(:attr_writer, *attr_writers) +        end + +        protected + +        attr_reader :attributes + +        private + +        def convert_to_reader_name(method_name) +          attr_name = method_name.to_s +          if attr_name.end_with?("=") +            attr_name = attr_name[0..-2] +          end +          attr_name +        end +      end      end      module HelperMethods diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb index b4301c23e4..52111e5442 100644 --- a/activemodel/lib/active_model/validations/callbacks.rb +++ b/activemodel/lib/active_model/validations/callbacks.rb @@ -23,6 +23,7 @@ module ActiveModel        included do          include ActiveSupport::Callbacks          define_callbacks :validation, +                         terminator: deprecated_false_terminator,                           skip_after_callbacks_if_terminated: true,                           scope: [:kind, :name]        end @@ -109,7 +110,7 @@ module ActiveModel        # Overwrite run validations to include callbacks.        def run_validations! #:nodoc: -        run_callbacks(:validation) { super } +        _run_validation_callbacks { super }        end      end    end diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb index 1b11c28087..8f8ade90bb 100644 --- a/activemodel/lib/active_model/validations/confirmation.rb +++ b/activemodel/lib/active_model/validations/confirmation.rb @@ -3,14 +3,16 @@ module ActiveModel    module Validations      class ConfirmationValidator < EachValidator # :nodoc:        def initialize(options) -        super +        super({ case_sensitive: true }.merge!(options))          setup!(options[:class])        end        def validate_each(record, attribute, value) -        if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed) -          human_attribute_name = record.class.human_attribute_name(attribute) -          record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name)) +        if (confirmed = record.send("#{attribute}_confirmation")) +          unless confirmation_value_equal?(record, attribute, value, confirmed) +            human_attribute_name = record.class.human_attribute_name(attribute) +            record.errors.add(:"#{attribute}_confirmation", :confirmation, options.except(:case_sensitive).merge!(attribute: human_attribute_name)) +          end          end        end @@ -24,6 +26,14 @@ module ActiveModel            :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")          end.compact)        end + +      def confirmation_value_equal?(record, attribute, value, confirmed) +        if !options[:case_sensitive] && value.is_a?(String) +          value.casecmp(confirmed) == 0 +        else +          value == confirmed +        end +      end      end      module HelperMethods @@ -55,6 +65,8 @@ module ActiveModel        # Configuration options:        # * <tt>:message</tt> - A custom error message (default is: "doesn't match        #   <tt>%{translated_attribute_name}</tt>"). +      # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by +      #   non-text columns (+true+ by default).        #        # There is also a list of default options supported by every validator:        # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+. diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index bda436d8d0..1da4df21e7 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -115,7 +115,7 @@ module ActiveModel            key = "#{key.to_s.camelize}Validator"            begin -            validator = key.include?('::') ? key.constantize : const_get(key) +            validator = key.include?('::'.freeze) ? key.constantize : const_get(key)            rescue NameError              raise ArgumentError, "Unknown validator: '#{key}'"            end | 
