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 missing nil attributes in SObject.update #79

Merged
merged 2 commits into from
Jan 9, 2024
Merged

Conversation

rferg
Copy link
Contributor

@rferg rferg commented Jan 8, 2024

Previously, calling SObject.update(id, a: nil) or SObject.update!(id, a: nil) would omit 'A' => nil from the Restforce request. Consequently, the field would not be updated to NULL in Salesforce, despite the clear intention of the caller to do that.

This is because .update creates an instance, passing in the given attributes, and then calls #update on it. That method only includes changed attributes in the request. And since a new instance's attribute is nil by default, setting it to nil does not register it as changed.

This PR fixes this by forcing any attributes given to SObject.update or SObject.update! with nil values to register a change on the model by using the ActiveModel::Dirty#*_will_change! method.

def prepare_for_update(id, attributes)
new(attributes.merge(id: id)).tap do |obj|
attributes.each do |name, value|
obj.public_send("#{name}_will_change!") if value.nil?
Copy link
Contributor Author

@rferg rferg Jan 8, 2024

Choose a reason for hiding this comment

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

I think this could be generalized to:

obj.public_send("#{name}_will_change!") unless object.public_send("#{name}_changed?")

in case there are other reasons that an attribute would be "unchanged". But this is more confusing and I think we should avoid those changed? calls if not really needed.

@@ -34,8 +34,24 @@ class << self
def_delegators :query, :not, :or, :where, :first, :last, :all, :find, :find!, :find_by, :find_by!, :sum, :count, :includes, :limit, :order, :select, :none
def_delegators :mapping, :table, :table_name, :custom_table?, :mappings

def update(id, attributes)
Copy link
Contributor Author

@rferg rferg Jan 8, 2024

Choose a reason for hiding this comment

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

I moved these up here so I could easily create a shared private class method (viz., prepare_for_update) and have them close together.

It's confusing that we have a class << self block and also a bunch of class methods defined outside of it with def self.method declarations. Might be better to migrate those into this block at some point.

@asedge
Copy link
Collaborator

asedge commented Jan 9, 2024

@rferg Please update the CHANGELOG.

@rferg
Copy link
Contributor Author

rferg commented Jan 9, 2024

@rferg Please update the CHANGELOG.

@asedge updated

@asedge asedge merged commit 8796078 into main Jan 9, 2024
5 checks passed
@asedge asedge deleted the fix-update-nil-handling branch January 9, 2024 14:58
@asedge
Copy link
Collaborator

asedge commented Jan 9, 2024

Thank you!

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

Successfully merging this pull request may close these issues.

3 participants