Skip to content

Instantly share code, notes, and snippets.

@AquaGeek
Created May 13, 2011 03:23
Show Gist options
  • Save AquaGeek/969909 to your computer and use it in GitHub Desktop.
Save AquaGeek/969909 to your computer and use it in GitHub Desktop.
Rails Lighthouse ticket #873
From a30e32aab00f9f1497e25361217399490bc01ec8 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:29:55 -0700
Subject: [PATCH] validates_exclusion_of can now accept a symbol representing an instance method or a proc object as the :in/:within option
---
.../lib/active_model/validations/exclusion.rb | 25 +++++++++++++--
activerecord/lib/active_record/validations.rb | 31 +++++++++++++++----
activerecord/test/cases/validations_test.rb | 31 ++++++++++++++++++++
3 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index f3367ab..1a4dd87 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -5,12 +5,20 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
+ # validates_exclusion_of :username, :in => :some_method_that_returns_an_enumerable_object
+ # validates_exclusion_of :username, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( admin superuser )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of items that the value shouldn't be part of
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved")
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
@@ -26,10 +34,19 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ record.errors.add(attr_name, configuration[:message] % value) if enum_object.include?(value)
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index b7e6394..7b0d188 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -745,15 +745,23 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
+ # validates_exclusion_of :username, :in => :some_method_that_returns_an_enumerable_object
+ # validates_exclusion_of :username, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( admin superuser )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of items that the value shouldn't be part of
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved")
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -766,10 +774,19 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- if enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ if enum_object.include?(value)
message = record.errors.generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
record.errors.add(attr_name, message)
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 4b2d28c..5899a84 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -688,6 +688,37 @@ class ValidationsTest < ActiveRecord::TestCase
assert t.errors.on(:title)
assert_equal "option monkey is restricted", t.errors["title"]
end
+
+ def test_validates_exclusion_of_with_proc_enumerable
+ Topic.validates_exclusion_of( :title, :in => Proc.new { |topic| [topic.class.to_s] } )
+
+ assert Topic.create("title" => "something", "content" => "abc").valid?
+ assert !Topic.create("title" => "Topic", "content" => "abc").valid?
+ end
+
+ def test_validates_exclusion_of_with_symbol_enumerable
+ Topic.class_eval "def title_exclusions; ['test']; end"
+
+ Topic.validates_exclusion_of( :title, :in => :title_exclusions )
+
+ assert Topic.create("title" => "something", "content" => "abc").valid?
+ assert !Topic.create("title" => "test", "content" => "abc").valid?
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => nil )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_a_symbol_and_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => :new_record? )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_a_proc_and_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => Proc.new { |topic| nil } )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
def test_validates_length_of_using_minimum
Topic.validates_length_of :title, :minimum => 5
--
1.5.4.3
From e9b9a481e48f172630a796ba2ac2e6b5e54e0e0b Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:46:24 -0700
Subject: [PATCH] validates_inclusion_of can now accept a symbol representing an instance method or a proc object as the :in/:within option
---
.../lib/active_model/validations/inclusion.rb | 25 +++++++++++--
activerecord/lib/active_record/validations.rb | 31 ++++++++++++----
activerecord/test/cases/validations_test.rb | 38 ++++++++++++++++++++
3 files changed, 83 insertions(+), 11 deletions(-)
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 9fc1caa..6c1eabe 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -5,12 +5,20 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => :some_method_that_returns_an_enumerable_object
+ # validates_inclusion_of :gender, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( m f )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of available items
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of available items
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list")
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is null (default is: +false+)
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
@@ -26,10 +34,19 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ record.errors.add(attr_name, configuration[:message] % value) unless enum_object.include?(value)
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 7b0d188..0a3a00e 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -710,15 +710,23 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => :some_method_that_returns_an_enumerable_object
+ # validates_inclusion_of :gender, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( m f )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of available items.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of available items
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list")
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is null (default is: +false+)
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -731,10 +739,19 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- unless enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ unless enum_object.include?(value)
message = record.errors.generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
record.errors.add(attr_name, message)
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 5899a84..ab9cfbe 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -652,6 +652,13 @@ class ValidationsTest < ActiveRecord::TestCase
assert Topic.create("title" => nil).valid?
assert Topic.create("title" => "abcde").valid?
end
+
+ def test_validates_inclusion_of
+ Topic.validates_inclusion_of( :title, :in => %w( abe monkey ) )
+
+ assert Topic.create("title" => "monkey", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
def test_validates_inclusion_of_with_formatted_message
Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
@@ -663,6 +670,37 @@ class ValidationsTest < ActiveRecord::TestCase
assert t.errors.on(:title)
assert_equal "option uhoh is not in the list", t.errors["title"]
end
+
+ def test_validates_inclusion_of_with_proc_enumerable
+ Topic.validates_inclusion_of( :title, :in => Proc.new { |topic| [topic.class.to_s] } )
+
+ assert Topic.create("title" => "Topic", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
+
+ def test_validates_inclusion_of_with_symbol_enumerable
+ Topic.class_eval "def title_inclusions; ['test']; end"
+
+ Topic.validates_inclusion_of( :title, :in => :title_inclusions )
+
+ assert Topic.create("title" => "test", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => nil )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_a_symbol_and_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => :new_record? )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_a_proc_and_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => Proc.new { |topic| nil } )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
def test_numericality_with_allow_nil_and_getter_method
Developer.validates_numericality_of( :salary, :allow_nil => true)
--
1.5.4.3
From 6e637df1fc7244d862fadd46e538fde46f8bbd24 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:54:15 -0700
Subject: [PATCH] Updated ActiveRecord CHANGELOG
---
activerecord/CHANGELOG | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 90be9b7..6245d7e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* validates_exclusion_of and validates_inclusion_of can now accept a symbol representing an instance method or a proc object as the :in or :within option
+
* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
--
1.5.4.3
From 9f77d913a49a13afc0f16778acd06af962272c47 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Thu, 21 Aug 2008 10:05:48 -0700
Subject: [PATCH] validates_exclusion/inclusion_of checks 'case enum' instead of 'case enum.class.to_s'
---
.../lib/active_model/validations/exclusion.rb | 10 +++++-----
.../lib/active_model/validations/inclusion.rb | 10 +++++-----
activerecord/lib/active_record/validations.rb | 20 ++++++++++----------
3 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 1a4dd87..380dbc3 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -35,11 +35,11 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
validates_each(attr_names, configuration) do |record, attr_name, value|
- enum_object = case enum.class.to_s
- when 'Symbol'
- then record.send(enum)
- when 'Proc'
- then enum.call(record)
+ enum_object = case enum
+ when Symbol
+ record.send(enum)
+ when Proc
+ enum.call(record)
else
enum
end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 6c1eabe..abdbd13 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -35,11 +35,11 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
validates_each(attr_names, configuration) do |record, attr_name, value|
- enum_object = case enum.class.to_s
- when 'Symbol'
- then record.send(enum)
- when 'Proc'
- then enum.call(record)
+ enum_object = case enum
+ when Symbol
+ record.send(enum)
+ when Proc
+ enum.call(record)
else
enum
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 0a3a00e..aad1c2f 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -740,11 +740,11 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
validates_each(attr_names, configuration) do |record, attr_name, value|
- enum_object = case enum.class.to_s
- when 'Symbol'
- then record.send(enum)
- when 'Proc'
- then enum.call(record)
+ enum_object = case enum
+ when Symbol
+ record.send(enum)
+ when Proc
+ enum.call(record)
else
enum
end
@@ -792,11 +792,11 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
validates_each(attr_names, configuration) do |record, attr_name, value|
- enum_object = case enum.class.to_s
- when 'Symbol'
- then record.send(enum)
- when 'Proc'
- then enum.call(record)
+ enum_object = case enum
+ when Symbol
+ record.send(enum)
+ when Proc
+ enum.call(record)
else
enum
end
--
1.5.4.3
From 382ff3faf9f300222507e3531006cfc27df2c517 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:29:55 -0700
Subject: [PATCH] validates_exclusion_of can now accept a symbol representing an instance method or a proc object as the :in/:within option
---
.../lib/active_model/validations/exclusion.rb | 25 +++++++++++++--
activerecord/lib/active_record/validations.rb | 31 +++++++++++++++----
activerecord/test/cases/validations_test.rb | 31 ++++++++++++++++++++
3 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index f3367ab..1a4dd87 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -5,12 +5,20 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
+ # validates_exclusion_of :username, :in => :some_method_that_returns_an_enumerable_object
+ # validates_exclusion_of :username, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( admin superuser )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of items that the value shouldn't be part of
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved")
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
@@ -26,10 +34,19 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ record.errors.add(attr_name, configuration[:message] % value) if enum_object.include?(value)
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index b7e6394..7b0d188 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -745,15 +745,23 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
+ # validates_exclusion_of :username, :in => :some_method_that_returns_an_enumerable_object
+ # validates_exclusion_of :username, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( admin superuser )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of items that the value shouldn't be part of
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved")
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is +nil+ (default is: +false+)
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -766,10 +774,19 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- if enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ if enum_object.include?(value)
message = record.errors.generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
record.errors.add(attr_name, message)
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 4b2d28c..5899a84 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -688,6 +688,37 @@ class ValidationsTest < ActiveRecord::TestCase
assert t.errors.on(:title)
assert_equal "option monkey is restricted", t.errors["title"]
end
+
+ def test_validates_exclusion_of_with_proc_enumerable
+ Topic.validates_exclusion_of( :title, :in => Proc.new { |topic| [topic.class.to_s] } )
+
+ assert Topic.create("title" => "something", "content" => "abc").valid?
+ assert !Topic.create("title" => "Topic", "content" => "abc").valid?
+ end
+
+ def test_validates_exclusion_of_with_symbol_enumerable
+ Topic.class_eval "def title_exclusions; ['test']; end"
+
+ Topic.validates_exclusion_of( :title, :in => :title_exclusions )
+
+ assert Topic.create("title" => "something", "content" => "abc").valid?
+ assert !Topic.create("title" => "test", "content" => "abc").valid?
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => nil )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_a_symbol_and_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => :new_record? )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_exclusion_of_should_raise_argument_error_if_in_option_is_a_proc_and_is_not_enumerable
+ Topic.validates_exclusion_of( :title, :in => Proc.new { |topic| nil } )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
def test_validates_length_of_using_minimum
Topic.validates_length_of :title, :minimum => 5
--
1.5.4.3
From f7c7db1ba3115c1c90e62fce0105d7486c001c75 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:46:24 -0700
Subject: [PATCH] validates_inclusion_of can now accept a symbol representing an instance method or a proc object as the :in/:within option
---
.../lib/active_model/validations/inclusion.rb | 25 +++++++++++--
activerecord/lib/active_record/validations.rb | 31 ++++++++++++----
activerecord/test/cases/validations_test.rb | 38 ++++++++++++++++++++
3 files changed, 83 insertions(+), 11 deletions(-)
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 9fc1caa..6c1eabe 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -5,12 +5,20 @@ module ActiveModel
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => :some_method_that_returns_an_enumerable_object
+ # validates_inclusion_of :gender, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( m f )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of available items
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of available items
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list")
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is null (default is: +false+)
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
@@ -26,10 +34,19 @@ module ActiveModel
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ record.errors.add(attr_name, configuration[:message] % value) unless enum_object.include?(value)
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 7b0d188..0a3a00e 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -710,15 +710,23 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :gender, :in => :some_method_that_returns_an_enumerable_object
+ # validates_inclusion_of :gender, :in => Proc.new { |person| person.some_method_that_returns_an_enumerable_object }
# validates_inclusion_of :age, :in => 0..99
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
+ #
+ # def some_method_that_returns_an_enumerable_object
+ # %w( m f )
+ # end
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of available items.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:in</tt> - An enumerable object, a symbol representing an instance method of the current class that returns
+ # an enumerable object, or a Proc that returns an enumerable object of available items
+ # * <tt>:within</tt> - A synonym (or alias) for <tt>:in</tt>
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list")
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the attribute is null (default is: +false+)
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the attribute is blank (default is: +false+)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
@@ -731,10 +739,19 @@ module ActiveRecord
enum = configuration[:in] || configuration[:within]
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
-
validates_each(attr_names, configuration) do |record, attr_name, value|
- unless enum.include?(value)
+ enum_object = case enum.class.to_s
+ when 'Symbol'
+ then record.send(enum)
+ when 'Proc'
+ then enum.call(record)
+ else
+ enum
+ end
+
+ raise(ArgumentError, "An object with the method include? is required must be supplied as the :in or :within option of the configuration hash") unless enum_object.respond_to?("include?")
+
+ unless enum_object.include?(value)
message = record.errors.generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
record.errors.add(attr_name, message)
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 5899a84..ab9cfbe 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -652,6 +652,13 @@ class ValidationsTest < ActiveRecord::TestCase
assert Topic.create("title" => nil).valid?
assert Topic.create("title" => "abcde").valid?
end
+
+ def test_validates_inclusion_of
+ Topic.validates_inclusion_of( :title, :in => %w( abe monkey ) )
+
+ assert Topic.create("title" => "monkey", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
def test_validates_inclusion_of_with_formatted_message
Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
@@ -663,6 +670,37 @@ class ValidationsTest < ActiveRecord::TestCase
assert t.errors.on(:title)
assert_equal "option uhoh is not in the list", t.errors["title"]
end
+
+ def test_validates_inclusion_of_with_proc_enumerable
+ Topic.validates_inclusion_of( :title, :in => Proc.new { |topic| [topic.class.to_s] } )
+
+ assert Topic.create("title" => "Topic", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
+
+ def test_validates_inclusion_of_with_symbol_enumerable
+ Topic.class_eval "def title_inclusions; ['test']; end"
+
+ Topic.validates_inclusion_of( :title, :in => :title_inclusions )
+
+ assert Topic.create("title" => "test", "content" => "abc").valid?
+ assert !Topic.create("title" => "something", "content" => "abc").valid?
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => nil )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_a_symbol_and_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => :new_record? )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
+
+ def test_validates_inclusion_of_should_raise_argument_error_if_in_option_is_a_proc_and_is_not_enumerable
+ Topic.validates_inclusion_of( :title, :in => Proc.new { |topic| nil } )
+ assert_raises(ArgumentError) { Topic.create("title" => "something", "content" => "abc") }
+ end
def test_numericality_with_allow_nil_and_getter_method
Developer.validates_numericality_of( :salary, :allow_nil => true)
--
1.5.4.3
From 80600bc8b55a3d5ffe0a834a81f8c695265bae85 Mon Sep 17 00:00:00 2001
From: Sean Huber <[email protected]>
Date: Wed, 20 Aug 2008 11:54:15 -0700
Subject: [PATCH] Updated ActiveRecord CHANGELOG
---
activerecord/CHANGELOG | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 90be9b7..6245d7e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* validates_exclusion_of and validates_inclusion_of can now accept a symbol representing an instance method or a proc object as the :in or :within option
+
* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
--
1.5.4.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment