Skip to content

Commit

Permalink
Merge branch 'deprecate-validator-setup'
Browse files Browse the repository at this point in the history
Closes #10716
  • Loading branch information
rafaelfranca committed May 23, 2013
2 parents 30d28b1 + 7d84c3a commit 9e5b8e3
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 42 deletions.
4 changes: 3 additions & 1 deletion activemodel/CHANGELOG.md
@@ -1,3 +1,5 @@
* No changes.
* Deprecate `Validator#setup`. This should be done manually now in the validator's constructor.

*Nick Sutterer*

Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activemodel/CHANGELOG.md) for previous changes.
4 changes: 3 additions & 1 deletion activemodel/lib/active_model/validations/acceptance.rb
Expand Up @@ -4,6 +4,7 @@ module Validations
class AcceptanceValidator < EachValidator # :nodoc:
def initialize(options)
super({ allow_nil: true, accept: "1" }.merge!(options))
setup!(options[:class])
end

def validate_each(record, attribute, value)
Expand All @@ -12,7 +13,8 @@ def validate_each(record, attribute, value)
end
end

def setup(klass)
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)
Expand Down
8 changes: 7 additions & 1 deletion activemodel/lib/active_model/validations/confirmation.rb
Expand Up @@ -2,14 +2,20 @@ module ActiveModel

module Validations
class ConfirmationValidator < EachValidator # :nodoc:
def initialize(options)
super
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))
end
end

def setup(klass)
private
def setup!(klass)
klass.send(:attr_reader, *attributes.map do |attribute|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
end.compact)
Expand Down
3 changes: 2 additions & 1 deletion activemodel/lib/active_model/validations/with.rb
Expand Up @@ -83,9 +83,10 @@ module ClassMethods
# end
def validates_with(*args, &block)
options = args.extract_options!
options[:class] = self

args.each do |klass|
validator = klass.new(options, &block)
validator.setup(self) if validator.respond_to?(:setup)

if validator.respond_to?(:attributes) && !validator.attributes.empty?
validator.attributes.each do |attribute|
Expand Down
32 changes: 23 additions & 9 deletions activemodel/lib/active_model/validator.rb
Expand Up @@ -82,18 +82,16 @@ module ActiveModel
# validates :title, presence: true
# end
#
# Validator may also define a +setup+ instance method which will get called
# with the class that using that validator as its argument. This can be
# useful when there are prerequisites such as an +attr_accessor+ being present.
# It can be useful to access the class that is using that validator when there are prerequisites such
# as an +attr_accessor+ being present. This class is accessable via +options[:class]+ in the constructor.
# To setup your validator override the constructor.
#
# class MyValidator < ActiveModel::Validator
# def setup(klass)
# klass.send :attr_accessor, :custom_attribute
# def initialize(options={})
# super
# options[:class].send :attr_accessor, :custom_attribute
# end
# end
#
# This setup method is only called when used with validation macros or the
# class level <tt>validates_with</tt> method.
class Validator
attr_reader :options

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

# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
@options = options.freeze
@options = options.except(:class).freeze
deprecated_setup(options)
end

# Return the kind for this validator.
Expand All @@ -123,6 +122,21 @@ def kind
def validate(record)
raise NotImplementedError, "Subclasses must implement a validate(record) method."
end

private
def deprecated_setup(options) # TODO: remove me in 4.2.
return unless respond_to?(:setup)
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:
class MyValidator < ActiveModel::Validator
def initialize(options={})
super
options[:class].send :attr_accessor, :custom_attribute
end
end
"
setup(options[:class])
end
end

# +EachValidator+ is a validator which iterates through the attributes given
Expand Down
24 changes: 1 addition & 23 deletions activemodel/test/cases/validations/with_validation_test.rb
Expand Up @@ -100,35 +100,13 @@ def check_validity!
test "passes all configuration options to the validator class" do
topic = Topic.new
validator = mock()
validator.expects(:new).with(foo: :bar, if: "1 == 1").returns(validator)
validator.expects(:new).with(foo: :bar, if: "1 == 1", class: Topic).returns(validator)
validator.expects(:validate).with(topic)

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

test "calls setup method of validator passing in self when validator has setup method" do
topic = Topic.new
validator = stub_everything
validator.stubs(:new).returns(validator)
validator.stubs(:validate)
validator.stubs(:respond_to?).with(:setup).returns(true)
validator.expects(:setup).with(Topic).once
Topic.validates_with(validator)
assert topic.valid?
end

test "doesn't call setup method of validator when validator has no setup method" do
topic = Topic.new
validator = stub_everything
validator.stubs(:new).returns(validator)
validator.stubs(:validate)
validator.stubs(:respond_to?).with(:setup).returns(false)
validator.expects(:setup).with(Topic).never
Topic.validates_with(validator)
assert topic.valid?
end

test "validates_with with options" do
Topic.validates_with(ValidatorThatValidatesOptions, field: :first_name)
topic = Topic.new
Expand Down
21 changes: 21 additions & 0 deletions activemodel/test/cases/validations_test.rb
Expand Up @@ -373,4 +373,25 @@ def test_dup_validity_is_independent
assert topic.invalid?
assert duped.valid?
end

# validator test:
def test_setup_is_deprecated_but_still_receives_klass # TODO: remove me in 4.2.
validator_class = Class.new(ActiveModel::Validator) do
def setup(klass)
@old_klass = klass
end

def validate(*)
@old_klass == Topic or raise "#setup didn't work"
end
end

assert_deprecated do
Topic.validates_with validator_class
end

t = Topic.new
t.valid?
end

end
7 changes: 1 addition & 6 deletions activerecord/lib/active_record/validations/uniqueness.rb
Expand Up @@ -7,11 +7,7 @@ def initialize(options)
"Pass a callable instead: `conditions: -> { where(approved: true) }`"
end
super({ case_sensitive: true }.merge!(options))
end

# Unfortunately, we have to tie Uniqueness validators to a class.
def setup(klass)
@klass = klass
@klass = options[:class]
end

def validate_each(record, attribute, value)
Expand All @@ -34,7 +30,6 @@ def validate_each(record, attribute, value)
end

protected

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

0 comments on commit 9e5b8e3

Please sign in to comment.