aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/validations/clusivity.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/validations/clusivity.rb')
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb54
1 files changed, 54 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
new file mode 100644
index 0000000000..bafb8e2106
--- /dev/null
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/range"
+
+module ActiveModel
+ module Validations
+ module Clusivity #:nodoc:
+ ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
+ "and must be supplied as the :in (or :within) option of the configuration hash"
+
+ def check_validity!
+ unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
+ raise ArgumentError, ERROR_MESSAGE
+ end
+ end
+
+ private
+
+ def include?(record, value)
+ members = if delimiter.respond_to?(:call)
+ delimiter.call(record)
+ elsif delimiter.respond_to?(:to_sym)
+ record.send(delimiter)
+ else
+ delimiter
+ end
+
+ members.send(inclusion_method(members), value)
+ end
+
+ def delimiter
+ @delimiter ||= options[:in] || options[:within]
+ end
+
+ # After Ruby 2.2, <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
+ # possible values in the range for equality, which is slower but more accurate.
+ # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
+ # endpoints, which is fast but is only accurate on Numeric, Time, Date,
+ # or DateTime ranges.
+ def inclusion_method(enumerable)
+ if enumerable.is_a? Range
+ case enumerable.first
+ when Numeric, Time, DateTime, Date
+ :cover?
+ else
+ :include?
+ end
+ else
+ :include?
+ end
+ end
+ end
+ end
+end