class ChangeTester
include Mongoid::Document
field :visit_stats, type: Array, default: []
end
irb(main):001:0> ChangeTester.delete_all
=> 1
irb(main):002:0> t = ChangeTester.create(visit_stats: [
])
=> #<ChangeTester _id: 4e7c57b28a364c07c1000001, _type: nil, visit_stats: [{:key_name=>"initial"}]>
irb(main):003:0> t.visit_stats_will_change!
=> [{:key_name=>"initial"}]
irb(main):004:0> t.visit_stats.each
=> [{:key_name=>"in-place"}]
irb(main):005:0> t.changes
=> {}
irb(main):006:0> t.changed_attributes
=> {"visit_stats"=>[{:key_name=>"in-place"}]}
The consequence of such behavior is that in-place changes to array can't be saved. The problem is that changed_attributes are stuck at array reference instead of cloned value (though AR code dutifully clones).
Direct assignment works well:
irb(main):001:0> ChangeTester.delete_all
=> 1
irb(main):002:0> t = ChangeTester.create(visit_stats: [
])
=> #<ChangeTester _id: 4e7c58558a364c081d000001, _type: nil, visit_stats: [{:key_name=>"initial"}]>
irb(main):003:0> t.visit_stats_will_change!
=> [{:key_name=>"initial"}]
irb(main):004:0> t.visit_stats = [
]
=> [{:key_name=>"assigned"}]
irb(main):005:0> t.changes
=> {"visit_stats"=>[[{:key_name=>"initial"}], [{:key_name=>"assigned"}]]}
I believe this is not a Mongoid bug per se, but incompatibility of AR code with complex datatype. It should be handled, though. Perhaps Mongoid should override will_change!, detect if array is in place, and store some extra info which is checked before save.