Skip to content

Commit 7d84c3a

Browse files
apotonickrafaelfranca
authored andcommitted
deprecate Validator#setup (to get rid of a respond_to call). validators do their setup in their constructor now.
1 parent 30d28b1 commit 7d84c3a

8 files changed

Lines changed: 61 additions & 42 deletions

File tree

activemodel/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
* No changes.
1+
* Deprecate `Validator#setup`. This should be done manually now in the validator's constructor.
2+
3+
*Nick Sutterer*
24

35
Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activemodel/CHANGELOG.md) for previous changes.

activemodel/lib/active_model/validations/acceptance.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Validations
44
class AcceptanceValidator < EachValidator # :nodoc:
55
def initialize(options)
66
super({ allow_nil: true, accept: "1" }.merge!(options))
7+
setup!(options[:class])
78
end
89

910
def validate_each(record, attribute, value)
@@ -12,7 +13,8 @@ def validate_each(record, attribute, value)
1213
end
1314
end
1415

15-
def setup(klass)
16+
private
17+
def setup!(klass)
1618
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
1719
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
1820
klass.send(:attr_reader, *attr_readers)

activemodel/lib/active_model/validations/confirmation.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ module ActiveModel
22

33
module Validations
44
class ConfirmationValidator < EachValidator # :nodoc:
5+
def initialize(options)
6+
super
7+
setup!(options[:class])
8+
end
9+
510
def validate_each(record, attribute, value)
611
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
712
human_attribute_name = record.class.human_attribute_name(attribute)
813
record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name))
914
end
1015
end
1116

12-
def setup(klass)
17+
private
18+
def setup!(klass)
1319
klass.send(:attr_reader, *attributes.map do |attribute|
1420
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
1521
end.compact)

activemodel/lib/active_model/validations/with.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ module ClassMethods
8383
# end
8484
def validates_with(*args, &block)
8585
options = args.extract_options!
86+
options[:class] = self
87+
8688
args.each do |klass|
8789
validator = klass.new(options, &block)
88-
validator.setup(self) if validator.respond_to?(:setup)
8990

9091
if validator.respond_to?(:attributes) && !validator.attributes.empty?
9192
validator.attributes.each do |attribute|

activemodel/lib/active_model/validator.rb

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,16 @@ module ActiveModel
8282
# validates :title, presence: true
8383
# end
8484
#
85-
# Validator may also define a +setup+ instance method which will get called
86-
# with the class that using that validator as its argument. This can be
87-
# useful when there are prerequisites such as an +attr_accessor+ being present.
85+
# It can be useful to access the class that is using that validator when there are prerequisites such
86+
# as an +attr_accessor+ being present. This class is accessable via +options[:class]+ in the constructor.
87+
# To setup your validator override the constructor.
8888
#
8989
# class MyValidator < ActiveModel::Validator
90-
# def setup(klass)
91-
# klass.send :attr_accessor, :custom_attribute
90+
# def initialize(options={})
91+
# super
92+
# options[:class].send :attr_accessor, :custom_attribute
9293
# end
9394
# end
94-
#
95-
# This setup method is only called when used with validation macros or the
96-
# class level <tt>validates_with</tt> method.
9795
class Validator
9896
attr_reader :options
9997

@@ -107,7 +105,8 @@ def self.kind
107105

108106
# Accepts options that will be made available through the +options+ reader.
109107
def initialize(options = {})
110-
@options = options.freeze
108+
@options = options.except(:class).freeze
109+
deprecated_setup(options)
111110
end
112111

113112
# Return the kind for this validator.
@@ -123,6 +122,21 @@ def kind
123122
def validate(record)
124123
raise NotImplementedError, "Subclasses must implement a validate(record) method."
125124
end
125+
126+
private
127+
def deprecated_setup(options) # TODO: remove me in 4.2.
128+
return unless respond_to?(:setup)
129+
ActiveSupport::Deprecation.warn "The `Validator#setup` instance method is deprecated and will be removed on Rails 4.2. Do your setup in the constructor instead:
130+
131+
class MyValidator < ActiveModel::Validator
132+
def initialize(options={})
133+
super
134+
options[:class].send :attr_accessor, :custom_attribute
135+
end
136+
end
137+
"
138+
setup(options[:class])
139+
end
126140
end
127141

128142
# +EachValidator+ is a validator which iterates through the attributes given

activemodel/test/cases/validations/with_validation_test.rb

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -100,35 +100,13 @@ def check_validity!
100100
test "passes all configuration options to the validator class" do
101101
topic = Topic.new
102102
validator = mock()
103-
validator.expects(:new).with(foo: :bar, if: "1 == 1").returns(validator)
103+
validator.expects(:new).with(foo: :bar, if: "1 == 1", class: Topic).returns(validator)
104104
validator.expects(:validate).with(topic)
105105

106106
Topic.validates_with(validator, if: "1 == 1", foo: :bar)
107107
assert topic.valid?
108108
end
109109

110-
test "calls setup method of validator passing in self when validator has setup method" do
111-
topic = Topic.new
112-
validator = stub_everything
113-
validator.stubs(:new).returns(validator)
114-
validator.stubs(:validate)
115-
validator.stubs(:respond_to?).with(:setup).returns(true)
116-
validator.expects(:setup).with(Topic).once
117-
Topic.validates_with(validator)
118-
assert topic.valid?
119-
end
120-
121-
test "doesn't call setup method of validator when validator has no setup method" do
122-
topic = Topic.new
123-
validator = stub_everything
124-
validator.stubs(:new).returns(validator)
125-
validator.stubs(:validate)
126-
validator.stubs(:respond_to?).with(:setup).returns(false)
127-
validator.expects(:setup).with(Topic).never
128-
Topic.validates_with(validator)
129-
assert topic.valid?
130-
end
131-
132110
test "validates_with with options" do
133111
Topic.validates_with(ValidatorThatValidatesOptions, field: :first_name)
134112
topic = Topic.new

activemodel/test/cases/validations_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,25 @@ def test_dup_validity_is_independent
373373
assert topic.invalid?
374374
assert duped.valid?
375375
end
376+
377+
# validator test:
378+
def test_setup_is_deprecated_but_still_receives_klass # TODO: remove me in 4.2.
379+
validator_class = Class.new(ActiveModel::Validator) do
380+
def setup(klass)
381+
@old_klass = klass
382+
end
383+
384+
def validate(*)
385+
@old_klass == Topic or raise "#setup didn't work"
386+
end
387+
end
388+
389+
assert_deprecated do
390+
Topic.validates_with validator_class
391+
end
392+
393+
t = Topic.new
394+
t.valid?
395+
end
396+
376397
end

activerecord/lib/active_record/validations/uniqueness.rb

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@ def initialize(options)
77
"Pass a callable instead: `conditions: -> { where(approved: true) }`"
88
end
99
super({ case_sensitive: true }.merge!(options))
10-
end
11-
12-
# Unfortunately, we have to tie Uniqueness validators to a class.
13-
def setup(klass)
14-
@klass = klass
10+
@klass = options[:class]
1511
end
1612

1713
def validate_each(record, attribute, value)
@@ -34,7 +30,6 @@ def validate_each(record, attribute, value)
3430
end
3531

3632
protected
37-
3833
# The check for an existing value should be run from a class that
3934
# isn't abstract. This means working down from the current class
4035
# (self), to the first non-abstract class. Since classes don't know

0 commit comments

Comments
 (0)