Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolves #1097: Add allow_nil qualifier to validate_presence_of #1100

Closed
wants to merge 2 commits into from
Closed

Conversation

cjmao
Copy link

@cjmao cjmao commented Apr 17, 2018

Please refer to #1097.
Also related: #683.

Copy link

@houndci-bot houndci-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some files could not be reviewed due to errors:

.rubocop.yml: Lint/EndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/EndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/DefEndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/BlockAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/ConditionPosition has the wrong namespace - should be Layout
Error: The `Style/TrailingCommaInLiteral` cop no longer exists. Please use `Style/TrailingCommaInArrayLiteral` and/or `Style/TrailingCommaInHashLiteral` instead.
(obsolete configuration found in .rubocop.yml, please update it)

@@ -146,12 +180,14 @@ def disallows_and_double_checks_value_of!(value, message)
end

def disallows_original_or_typecast_value?(value, message)
disallows_value_of(blank_value, @expected_message)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a misuse.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, you're right!

end

def blank_value
if collection?
[]
elsif expects_to_allow_nil?
''
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe need a better implementation?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, perhaps the matcher should work like this?

  • If the matcher is qualified with allow_nil, then it should test that empty-string and nil are disallowed.
  • However, if it is qualified with allow_nil, then it should only test that empty-string is disallowed (because it tests that nil is allowed).

What do you think about that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps what you mean is, when the matcher is not qualified with allow_nil it should disallow both '' and nil?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, both a blank string and a nil value return true to #blank?,
so as long as the ActiveModel::Validations::PresenceValidator uses #blank? to check the validity of an attribute, both a true blank string and a nil can be considered a blank string (when the attribute's type is either String or Nil).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps what you mean is, when the matcher is not qualified with allow_nil it should disallow both '' and nil?

Yes, that's what I meant :)

IMO, both a blank string and a nil value return true to #blank?

Right, exactly. As it stands, the presence matcher only uses nil in making its assertion, and in your changes here you conditionally flip that to an empty string, but really, in the default case where allow_nil is not specified, the presence matcher needs to make use of both, since PresenceValidator is not just testing that the attribute is nil but is testing that it is blank.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good! Perhaps instead of reduce you could use all? And I don't think you have to write any tests to cover this -- we are changing how the matcher works internally, and as long as these changes still make the current tests pass then you should be good.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried to use the code similar to the previous example to see if it still passes the tests.
However, after I ran the specs, there is one failure. And I think the cause might be testing agains an empty string when not qualified with allow_nil.
So I checked out c0960bd7 (the commit right before this PR), and made the following modification only:

 def blank_value
   if collection?
     []
   else
-    nil
+     ''
   end
 end

After I ran the specs again, there are several failed specs expecting an assertion to fail with a message but the assertion did not fail.
So I think maybe we should not explicitly disallow an empty string when not using allow_nil.

Copy link
Author

@cjmao cjmao Apr 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there are several places that have an example similar to this:

it_supports(
  'ignoring_interference_by_writer',
  tests: {
    accept_if_qualified_but_changing_value_does_not_interfere: {
      changing_values_with: :nil_to_blank
    },
    reject_if_qualified_but_changing_value_interferes: {
      model_name: 'Example',
      attribute_name: :attr,
      changing_values_with: :never_falsy,
      expected_message: <a message>
    }
  }
)

Though I don't fully understand how this works, I think this might already have covered the case where setting an attribute to an empty string: changing_values_with: :nil_to_blank .

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you go ahead and push up your modifications so that I can see the test failures? That way I can debug them for you.

Also in response to your last comment, that test is making sure that if you have an attribute that changes nil to an empty string when it is assigned, that this won't cause the presence validation to fail. I guess it's true that this is implicitly testing the empty string case, but I think it would be more clear to have an explicit test case / context so that it would be easier to find if we have to do so later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I've just pushed my code.

Copy link

@houndci-bot houndci-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some files could not be reviewed due to errors:

.rubocop.yml: Lint/EndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/EndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/DefEndAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/BlockAlignment has the wrong namespace - should be Layout
.rubocop.yml: Lint/ConditionPosition has the wrong namespace - should be Layout
Error: The `Style/TrailingCommaInLiteral` cop no longer exists. Please use `Style/TrailingCommaInArrayLiteral` and/or `Style/TrailingCommaInHashLiteral` instead.
(obsolete configuration found in .rubocop.yml, please update it)

@mcmire
Copy link
Collaborator

mcmire commented Apr 23, 2018

This looks great. The only issue with this is that it looks like we are running into #904 on one of the tests. This isn't your fault, but it does mean that I would have to get a fix for that into master before merging this so that CI can run cleanly. Sorry :( Hopefully I can have this finished soon.

@mcmire
Copy link
Collaborator

mcmire commented Sep 13, 2018

#904 will be fixed soon, so this is just a note that in in order to merge this, we are going to need these changes to show up in does_not_match? as well as per #1136. We can take care of this when we merge this in.

@mcmire mcmire added this to the v4.next milestone Feb 18, 2019
@mcmire mcmire modified the milestones: v4.next, v4.1.0 Apr 23, 2019
@mcmire
Copy link
Collaborator

mcmire commented Jun 1, 2019

Sorry it took me so long to merge this, but I took your ideas here and ran with them: 834d8d0. Thanks so much!

@mcmire mcmire closed this Jun 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants