Skip to content

Instantly share code, notes, and snippets.

@millisami
Created November 30, 2010 10:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save millisami/721466 to your computer and use it in GitHub Desktop.
Save millisami/721466 to your computer and use it in GitHub Desktop.
Mongoid Dirty Tracking
%w(rubygems mongoid rspec).each do |gem|
require gem
end
require 'hijacker'
Hijacker.configure do
uri 'druby://localhost:8787'
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.inject({}) do |memo, child|
memo.merge(child.each{|c| c.send(:changes) if c.send(:changed?)})
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
referenced_changes
else
original_value.merge(embedded_changes).merge(referenced_changes)
end
end
def changed?
super || self._children.any?(&:changed?)
end
end
Hijacker.spy(Person,
:only => :instance_methods,
:uri => 'druby://localhost:8787')
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment