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

Fix Settable: allow setting a nested value to a non-string (for 6.4 stable) #4510

Merged
merged 2 commits into from Sep 13, 2018

Conversation

mdehoog
Copy link
Contributor

@mdehoog mdehoog commented Jul 17, 2018

#4487 introduced a regression: one can no longer $set a nested hash value to anything that doesn't implement empty?.

This PR fixes a couple of bugs with the previous PR:

  • Pull out the value from field_and_value_hash before checking if it's empty?; this ensures we're checking the actual value that is attempting to be set
  • Check that it is actually a Hash before checking if it's empty?

The reason previous tests passed is because they only checked that nested values could be set to a string, which implements empty?.

Relevant stacktrace here:

      undefined method `empty?' for 1531794645:Fixnum
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable/settable.rb:29:in `block (2 levels) in set'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable.rb:166:in `block (2 levels) in process_atomic_operations'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/attributes/readonly.rb:36:in `as_writable_attribute!'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable.rb:165:in `block in process_atomic_operations'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable.rb:164:in `each'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable.rb:164:in `process_atomic_operations'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable/settable.rb:24:in `block in set'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable.rb:142:in `prepare_atomic_operation'
./tmp/bundle/ruby-2.3.7/ruby/2.3.0/gems/mongoid-6.4.1/lib/mongoid/persistable/settable.rb:23:in `set'


if fields[field] && fields[field].type == Hash && attributes.key?(field) && !value.empty?
if fields[field] && fields[field].type == Hash && attributes.key?(field) && Hash === value && !value.empty?
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think respond_to?(:empty?) would make more sense here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I think we specifically want to check for a Hash... for example we don't want to enter this block for a String, even though String implements empty?.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK that makes sense. Is this behavior documented somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure. The behavior in this PR seems to make sense: only merge an existing Hash with a new value if that value is a non-empty Hash. The PR that introduced the regression intended to support setting field values to an empty Hash, which remains supported.

@gracezhangyaofei
Copy link

Hi may I know when is this PR going to be merged? This issue is quite serious though. Many thanks!

@p-mongo
Copy link
Contributor

p-mongo commented Jul 25, 2018

@gracezhangyaofei I need to dig into the code that this PR changes as the change itself is non-obvious. Additionally neither https://docs.mongodb.com/mongoid/master/tutorials/mongoid-persistence/ nor https://docs.mongodb.com/mongoid/master/tutorials/mongoid-nested-attributes/ seem to mention anything that looks like church.set('location.address.city' => 12345), I need to figure out where that is documented.

@gracezhangyaofei
Copy link

gracezhangyaofei commented Jul 25, 2018

Hi @p-mongo thanks for your fast reply. Didn't dig into these tutorials. But it is a real problem for me right now. And it was caused by !value.empty? not too long ago. Now I was forced to do an unfriendly workaround, hopefully this can be fixed asap. Thanks and looking forward!

@mdehoog
Copy link
Contributor Author

mdehoog commented Jul 25, 2018

Documentation about how $set should behave in embedded documents is here: https://docs.mongodb.com/manual/reference/operator/update/set/#set-fields-in-embedded-documents. Currently 6.4.0/6.4.1 will throw an error if 'zzz' is changed to anything that doesn't implement empty?. The current spec missed this because Ruby String implements empty?.

The new spec should indicate what this PR is attempting to fix. AFAIK there's no existing behavior change here.

end

before do
church.set('location.address.city' => 12345)
Copy link
Contributor

Choose a reason for hiding this comment

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

If I'm understanding correctly, this is where the exception would have occurred before the change? If so, let's put this inside a let block instead and invoke inside the it block below so that any future regression occurs in a place that's more easily identified.

Copy link
Contributor Author

@mdehoog mdehoog Jul 28, 2018

Choose a reason for hiding this comment

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

Can do, but this way it's more consistent with all the examples around it. I think an exception raised anywhere in the spec is fine for failing a test; it doesn't necessarily need to be raised in the it block. However, happy to change it if this blocks merging.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough; I don't feel strongly enough to block merging on this, so it's fine to leave as-is.

@saghm
Copy link
Contributor

saghm commented Jul 26, 2018

@p-mongo From what I can tell, this change seems to be correct, and I think we should update our documentation to describe this behavior. That being said, I think the documentation you linked to describes a separate feature from this, namely updating both sides of a relation at the same time (which unfortunately is not at all what the name seems to imply). I'm thinking that the best place for the new documentation would be in the "Fields" section. Thoughts?

@p-mongo
Copy link
Contributor

p-mongo commented Jul 26, 2018

I think the docs should be under persistence/#set, I'm going through the relevant bits in the tutorial to expand them.

@p-mongo
Copy link
Contributor

p-mongo commented Jul 26, 2018

https://jira.mongodb.org/browse/MONGOID-4579 for that, if you want to merge this PR as it is that's ok with me.

@p-mongo
Copy link
Contributor

p-mongo commented Jul 30, 2018

@mdehoog Can you provide a reduced failing example that this PR is fixing?

@p-mongo
Copy link
Contributor

p-mongo commented Jul 30, 2018

Or @gracezhangyaofei if you have an example because so far my examples are working.

@p-mongo
Copy link
Contributor

p-mongo commented Jul 30, 2018

Docs are in #4514.

@mdehoog
Copy link
Contributor Author

mdehoog commented Jul 30, 2018

@p-mongo The specs I added fail without the change:

Failures:

  1) Mongoid::Persistable::Settable when the field is already set locally when the field is a nested hash when a leaf value in the nested hash is updated to a number updates the nested value to the correct value
     Failure/Error: if fields[field] && fields[field].type == Hash && attributes.key?(field) && !value.empty?

     NoMethodError:
       undefined method `empty?' for 12345:Fixnum
     # ./lib/mongoid/persistable/settable.rb:29:in `block (2 levels) in set'
     # ./lib/mongoid/persistable.rb:166:in `block (2 levels) in process_atomic_operations'
     # ./lib/mongoid/attributes/readonly.rb:36:in `as_writable_attribute!'
     # ./lib/mongoid/persistable.rb:165:in `block in process_atomic_operations'
     # ./lib/mongoid/persistable.rb:164:in `each'
     # ./lib/mongoid/persistable.rb:164:in `process_atomic_operations'
     # ./lib/mongoid/persistable/settable.rb:24:in `block in set'
     # ./lib/mongoid/persistable.rb:142:in `prepare_atomic_operation'
     # ./lib/mongoid/persistable/settable.rb:23:in `set'
     # ./spec/mongoid/persistable/settable_spec.rb:329:in `block (5 levels) in <top (required)>'

Finished in 1.55 seconds (files took 0.88375 seconds to load)
56 examples, 1 failure

Failed examples:

rspec ./spec/mongoid/persistable/settable_spec.rb:332 # Mongoid::Persistable::Settable when the field is already set locally when the field is a nested hash when a leaf value in the nested hash is updated to a number updates the nested value to the correct value

@p-mongo
Copy link
Contributor

p-mongo commented Jul 30, 2018

I see, the bug affects 6.x only. This problem does not exist on master. Have you investigated what makes master work in this case?

@mdehoog
Copy link
Contributor Author

mdehoog commented Jul 30, 2018

#4487 wasn't merged into master, so master might actually suffer from https://jira.mongodb.org/browse/MONGOID-4525. I haven't validated this.

@p-mongo
Copy link
Contributor

p-mongo commented Jul 30, 2018

OK, then we need to figure out if 4525 affects master, ensure master has an equivalent test to the one added in this PR, then see if the fix can be backported from master or potentially master needs some version of #4487 and/or this PR.

@mdehoog
Copy link
Contributor Author

mdehoog commented Aug 2, 2018

Yes the original issue affects master and has not been fixed; verified by running the new specs from #4487 on master. #4515 should fix master and includes this change too.

@p-mongo
Copy link
Contributor

p-mongo commented Aug 10, 2018

After #4515 lands it needs to be cherry picked back into this PR.

@saghm
Copy link
Contributor

saghm commented Aug 14, 2018

Closed in favor of #4518

@saghm saghm closed this Aug 14, 2018
@mdehoog
Copy link
Contributor Author

mdehoog commented Aug 14, 2018

@saghm is it worthwhile getting this fix in to the 6.4.0 branch anyway? It is currently broken on this branch, and #4518 is a different fix that introduces a breaking change, so not sure if it's wise to backport.

@saghm
Copy link
Contributor

saghm commented Aug 14, 2018

We don't currently have any bugfix release scheduled for the 6.4.x branch, although we don't have any opposition to doing one. Is this something you need, or would using the 7.0.x branch work for you?

@mdehoog
Copy link
Contributor Author

mdehoog commented Aug 14, 2018

Sounds good 👍. We're not in a position to move to 7.0.x yet, but we'll put it on the roadmap.

@mdehoog mdehoog deleted the fix-nested-non-string-setter branch August 14, 2018 23:08
@saghm
Copy link
Contributor

saghm commented Aug 16, 2018

After some discussion, we've decided we do in fact want to do a patch release for 6.4, and this seems like a good candidate to be part of that. Unfortunately, it looks like I can't reopen this since the branch is closed. Any chance it's possible to get it pushed back up? (If not, that's totally fine! I can just manually apply the patch, which Github does seem to have cached).

@mdehoog mdehoog restored the fix-nested-non-string-setter branch August 17, 2018 02:38
@mdehoog
Copy link
Contributor Author

mdehoog commented Aug 17, 2018

@saghm I restored the branch, but can't reopen the PR; do you have access to do this?

@p-mongo p-mongo reopened this Aug 17, 2018
@p-mongo p-mongo changed the title Fix Settable: allow setting a nested value to a non-string Fix Settable: allow setting a nested value to a non-string (for 6.4 stable) Aug 20, 2018
@p-mongo
Copy link
Contributor

p-mongo commented Aug 20, 2018

Let's cherry-pick this diff on 7.0.0-stable as well.

@gracezhangyaofei
Copy link

Hi @p-mongo , may I know when this fix can be merged? I saw your last msg was two weeks ago. Keep me posted, many thanks!

@p-mongo
Copy link
Contributor

p-mongo commented Sep 5, 2018

@gracezhangyaofei We are configuring CI internally to get this PR appropriately tested before merging it.

@saghm
Copy link
Contributor

saghm commented Sep 11, 2018

Hi @mdehoog! Sorry for the delay in merging this; we had to get our CI set up for the 6.4.0-stable branch, but it's all ready go to now! Is it all right with you if I push a rebase up to your branch with a few backports we made to the 6.4.0 branch to get the CI passing? (Github suggests that I can "add more commits by pushing" to your branch, but I'm not sure about a force push after a rebase, and I don't want to force push to your repo without getting permission first anyhow)

@mdehoog
Copy link
Contributor Author

mdehoog commented Sep 11, 2018

@saghm completely fine, go for it 👍

@saghm
Copy link
Contributor

saghm commented Sep 12, 2018

Awesome, thanks!

@saghm saghm force-pushed the fix-nested-non-string-setter branch from 051ff8b to 78607ba Compare September 12, 2018 19:23
@saghm saghm merged commit 48673d7 into mongodb:6.4.0-stable Sep 13, 2018
@saghm
Copy link
Contributor

saghm commented Sep 13, 2018

Just merged this. Thanks again for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants