aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/validations/acceptance.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/validations/acceptance.rb')
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb70
1 files changed, 61 insertions, 9 deletions
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index ac5e79859b..c5c0cd4636 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -3,22 +3,73 @@ module ActiveModel
module Validations
class AcceptanceValidator < EachValidator # :nodoc:
def initialize(options)
- super({ allow_nil: true, accept: "1" }.merge!(options))
+ super({ allow_nil: true, accept: ["1", true] }.merge!(options))
setup!(options[:class])
end
def validate_each(record, attribute, value)
- unless value == options[:accept]
+ unless acceptable_option?(value)
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
end
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
@@ -38,9 +89,10 @@ module ActiveModel
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "must be
# accepted").
- # * <tt>:accept</tt> - Specifies value that is considered accepted.
- # The default value is a string "1", which makes it easy to relate to
- # an HTML checkbox. This should be set to +true+ if you are validating
+ # * <tt>:accept</tt> - Specifies a value that is considered accepted.
+ # Also accepts an array of possible values. The default value is
+ # an array ["1", true], which makes it easy to relate to an HTML
+ # checkbox. This should be set to, or include, +true+ if you are validating
# a database column, since the attribute is typecast from "1" to +true+
# before validation.
#