modernistik / parse-stack

Parse Server Ruby Client SDK
https://www.modernistik.com/gems/parse-stack/
MIT License
61 stars 20 forks source link

Modifying value in place doesn't mark field dirty #42

Closed hspindell closed 6 years ago

hspindell commented 6 years ago

Example 1 with gsub! :

> m.content
 => "interesting..." 
> m.content.gsub!("...","")
 => "interesting" 
> m.changed_attributes
 => {} 

Example 2 with gsub (non-modifying):

> m.content
 => "interesting..." 
> m.content = m.content.gsub("...","")
 => "interesting" 
> m.changed_attributes
 => {"content"=>"interesting..."} 

Or if this is intended, what's the reasoning for it?

apersaud commented 6 years ago

That is acceptable ruby as your are modifying a field value by reference (self-mutating) and intentionally bypassing dirty tracking. In your example, you are not invoking dirty tracking methods manually when calling ruby mutating methods.

See ActiveModel documentation: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

If an attribute is modified in-place then make use of [attribute_name]_will_change! to mark that the attribute is changing. Otherwise Active Model can't track changes to in-place attributes.

In your example 1, while m.content is now "interesting" using the mutating method gsub!, it never calls the dirty tracking system. You will need to call m.content_will_change! before you mutate the internal value. In Example 2, the reason dirty tracking is working is because you are using the method content=, which internally triggers dirty tracking on your behalf by ActiveModel.

When dealing with mutating methods, you should trigger dirty tracking yourself to let the system know that the underlying value will change:

m.content # => "interesting..."
m.content_will_change! # => "interesting" (dirty tracking manually invoked)
m.content.gsub!("...","") # => "interesting"
m.content_changed? # => true
m.changed_attributes # => {'content' => "interesting..."} (original value)
m.changes # => {"content"=>["interesting...", "interesting"]}