-
-
Save bowsersenior/721971 to your computer and use it in GitHub Desktop.
%w(rubygems mongoid rspec).each do |gem| | |
require gem | |
end | |
Mongoid.configure do |config| | |
name = "dirty_track_test" | |
host = "localhost" | |
config.master = Mongo::Connection.new.db(name) | |
config.persist_in_safe_mode = false | |
end | |
RSpec.configure do |config| | |
config.before(:each) do | |
Mongoid.master.collections.reject { |c| c.name == 'system.indexes'}.each(&:drop) | |
end | |
end | |
class Person | |
include Mongoid::Document | |
field :name | |
embeds_one :address | |
embeds_many :phones | |
references_many :projects | |
# dirty tracking of embedded documents | |
attr_writer :embedded_changes, :referenced_changes | |
def referenced_changes | |
puts "SELF: #{(self.methods - Object.methods).sort.to_yaml}" | |
puts "CHILDREN: #{((self.associations)).inspect}" | |
@referenced_changes ||= begin | |
self.associations.keys.each do |assoc_name| | |
self.send(assoc_name).inject({}) do |memo, obj| | |
memo.merge(obj.each{|c| c.send(:changes) if c.send(:changed?)}) | |
end | |
end | |
end | |
end | |
def embedded_changes | |
@embedded_changes ||= begin | |
self._children.inject({}) do |memo, child| | |
memo.merge(child.changes) if child.changed? | |
end | |
end | |
end | |
def changes | |
original_value = super | |
if original_value.blank? | |
embedded_changes # need to merge these two hashes | |
referenced_changes # but make sure to detect nil values | |
else | |
original_value.merge(embedded_changes).merge(referenced_changes) | |
end | |
end | |
def changed? | |
super || self._children.any?(&:changed?) # what about changes to referenced docs? | |
end | |
end | |
class Address | |
include Mongoid::Document | |
field :city | |
embedded_in :person, :inverse_of => :address | |
end | |
class Phone | |
include Mongoid::Document | |
field :number, :type => Integer | |
embedded_in :person, :inverse_of => :phones | |
end | |
class Project | |
include Mongoid::Document | |
field :title | |
referenced_in :person, :inverse_of => :projects | |
end | |
describe Person do | |
context "parent document" do | |
it "should detect the changes" do | |
@person = Person.new(:name => "Sachin") | |
@person.save! | |
@person.name = "Sagar" | |
@person.changed.should be_true | |
end | |
end | |
context "parent document with embedded one document" do | |
it "should detect the changes of the embedded one address document when the address city is changed" do | |
@person = Person.new(:name => "Sachin") | |
@person.create_address(:city => "Kathmandu") | |
@person.changed?.should be_false | |
@person.address.city = "Lalitpur" | |
@person.changed?.should be_true | |
@person.changes["city"].should include("Lalitpur") | |
end | |
end | |
context "parent document with embedded many document" do | |
it "should detect the changes of the embedded many phones document when the phone number is changed" do | |
@person = Person.new(:name => "Sachin") | |
@person.phones.create!(:number => 980) | |
@person.phones.first.number.should == 980 | |
@person.changed?.should be_false | |
@person.phones.first.number = 400 | |
@person.changed?.should be_true | |
@person.changes["number"].should include(400) | |
end | |
end | |
context "parent document with references many documents" do | |
it "should detect the changes of the referenced many project document when the project title is changed" do | |
@person = Person.create!(:title => "Sachin") | |
@project = Project.new(:name => "Gliding") | |
@person.projects << @project | |
@person.changes | |
@person.changed?.should be_true | |
# @person.changes["title"].should include("Gliding") | |
end | |
end | |
end |
I looked further into this and I don't think it will work. The problem is that references work very differently than embedded docs and there are a lot of difficulties in tracking their changes. Sorry, but I can only recommend that you go with the embedded_changes only or find another way of solving the problem. FYI, there is a dirty_associations plugin for ActiveRecord: https://github.com/daphonz/dirty_associations
@bowsersenior, that dirty_associations plugin is for ActiveRecord and it blows out when I install it.
Yes, I know dirty_associations is for activerecord. If you look at the code for dirty_associations, you can get some ideas on how to do something similar for mongoid. But I think you'll see that it won't be easy! Also, dirty_associations doesn't record changes to an associated record, only the addition or removal of an associated record.
This is the diff patch you made.
diff --git a/dirty-track.rb b/dirty-track.rb
index f461b97..598c3b7 100644
--- a/dirty-track.rb
+++ b/dirty-track.rb
@@ -30,8 +30,10 @@ class Person
puts "CHILDREN: #{((self.associations)).inspect}"
But still it doesn't work. Yup, I know that you don't have time. But can you plz look at it and just run
ruby dirty-track.rb
to make the spec green!!