Remove explicit respond_to? and Validator#setup call #10716

Closed
wants to merge 7 commits into
from

Projects

None yet

3 participants

@apotonick
Contributor

This moves the setup process of validators into the constructor, pushing all knowledge about this into the validator class itself. Makes it easier to override internals in subclassed validators.

@apotonick apotonick remove explicit call to Validator#setup to get rid of respond_to call…
…. instead, respective validators take care of their setup.
543c59d
@rafaelfranca
Member

Could you give us example of what you want to accomplish with this change?

Maybe an example how was the validator subclass before and after it.

@apotonick
Contributor

This is the "bottom line" of my PR: apotonick@543c59d#L2L88

I could get my stuff working using the existing code. However, my main intend is to remove the respond_to? call and move stuff into Validator itself. While this simplifies the Validator interface it is also preparing some more changes I'd like to push (especially simplifying UniquenessValidator in AR).

Are you worried about the change? Is it because the options contain a :class now (I don't like it either)? It shouldn't break existing code?!

@rafaelfranca rafaelfranca commented on an outdated diff May 22, 2013
activemodel/lib/active_model/validator.rb
@@ -123,6 +125,10 @@ def kind
def validate(record)
raise NotImplementedError, "Subclasses must implement a validate(record) method."
end
+
+ private
@rafaelfranca
rafaelfranca May 22, 2013 Member

This should be in the same indentation of the def.

@rafaelfranca rafaelfranca commented on an outdated diff May 22, 2013
activemodel/lib/active_model/validator.rb
@@ -123,6 +125,10 @@ def kind
def validate(record)
raise NotImplementedError, "Subclasses must implement a validate(record) method."
end
+
+ private
+ def setup!
@rafaelfranca
rafaelfranca May 22, 2013 Member

This should be indented one level mode than the private keyword

@rafaelfranca
rafaelfranca May 22, 2013 Member

I think we should change the documentation of this method on the Validator class

@rafaelfranca rafaelfranca commented on an outdated diff May 22, 2013
activemodel/lib/active_model/validations/confirmation.rb
@@ -9,13 +9,13 @@ def validate_each(record, attribute, value)
end
end
- def setup(klass)
- klass.send(:attr_reader, *attributes.map do |attribute|
- :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
+ def setup!
@rafaelfranca
rafaelfranca May 22, 2013 Member

Should not this method be private now?

@rafaelfranca
Member

Are you worried about the change?

No. I just want to know more about the motivation of this pull request.

Is it because the options contain a :class now (I don't like it either)?

It is awkward, but it is fine to me.

It shouldn't break existing code?!

I'm not sure of this. setup(klass) is a public method and documented as part of the API (see Validator documentation). We can't just remove it.

@apotonick
Contributor

You're right! #setup! should be made private soon and called by a public #setup which will be deprecated.

def setup(*)
  deprecation_warning
  setup!
end

private
def setup!
end
@rafaelfranca
Member

👍

@apotonick
Contributor

What is the prefered way of deprecating (and testing this)? Sorry to ask :-)

@rafaelfranca
Member

Maybe:

if validator.respond_to?(:setup)
  ActiveSupport::Deprecation.warn 'The `setup` instance method is deprecated and will be removed on Rails 4.2. Define `setup!` without arguments instead.'
  validator.setup(self)
end

You can even provide a code snippet in the deprecation message to make explicit what the users have to do:

Change your setup method to:

class MyValidator < ActiveModel::Validator
  private

  def setup!
    @klass.send :attr_accessor, :custom_attribute
  end
end

To test you can define a Validator class with the setup method defined and assert the deprecation message with the assert_deprecated method when calling validates_with. Something like this:

def test_setup_is_deprecated
  assert_deprecated do
    Class.new do
      include ActiveModel::Validations

      validates_with MyDeprecatedValidator
    end
  end
end
@apotonick
Contributor

Now the last quest: Why is options.freeze called in Validator#initialize?

@rafaelfranca I wanted to say I really appreciate your responsiveness - amazing! 😸

@josevalim josevalim commented on an outdated diff May 22, 2013
activemodel/lib/active_model/validator.rb
@@ -107,7 +105,9 @@ def self.kind
# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
- @options = options.freeze
+ @klass = options[:class]
@josevalim
josevalim May 22, 2013 Member

I don't see the need for always storing the @klass if we only need it in some validators and temporarily.

@josevalim josevalim and 1 other commented on an outdated diff May 22, 2013
activemodel/lib/active_model/validator.rb
@@ -107,7 +105,9 @@ def self.kind
# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
- @options = options.freeze
+ @klass = options[:class]
+ @options = options.except(:class).freeze
+ deprecated_setup or setup!
@josevalim
josevalim May 22, 2013 Member

I am not sure if we stil have Rails guidelines around but part of them was about never using and or or due to precedence rules.

@josevalim
josevalim May 22, 2013 Member

Also, can't we just let developers override initialize? Why still call it?

@apotonick
apotonick May 22, 2013 Contributor

I had this in an earlier version, for me it felt ok but I wasn't sure if you guys like the additional call. So, override initialize in validators:

def initialize(options)
  super
  @klass = options[:class]
@josevalim
Member

I have added some comments, :+1 for getting rid of the respond_to? in general.

@apotonick
Contributor

Cool! I removed the automatic setup! call, I personally like that better. Ready for merge!

@rafaelfranca rafaelfranca commented on the diff May 22, 2013
activemodel/lib/active_model/validator.rb
@@ -123,6 +122,23 @@ 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. Change your `setup` method to something like:
+
@rafaelfranca
rafaelfranca May 22, 2013 Member

@apotonick should not the deprecation warning be updated to show the initialize override example?

@rafaelfranca
Member

I like it too. Just a minor comment on the deprecation message.

@rafaelfranca
Member

Since it is a user facing change we will need a CHANGELOG entry, and make sure your commits are squashed.

@apotonick
Contributor

Dunno how to change a PR to another branch, here it goes: https://github.com/apotonick/rails/tree/deprecate-validator-setup rebased to current rails/rails:master and including updated exception and CHANGELOG entry.

@rafaelfranca
Member

@apotonick merged.

Thank you for contributing.

@apotonick
Contributor

Thank you @josevalim and especially @rafaelfranca for that quick and uncomplicated feedback and processing! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment