2011-02-28 16:00:41 +01:00
|
|
|
require File.expand_path("../../spec_helper", __FILE__)
|
|
|
|
|
|
|
|
require File.join(FIXTURE_PATH, 'more', 'cat')
|
|
|
|
require File.join(FIXTURE_PATH, 'more', 'article')
|
|
|
|
require File.join(FIXTURE_PATH, 'more', 'course')
|
|
|
|
require File.join(FIXTURE_PATH, 'more', 'card')
|
|
|
|
require File.join(FIXTURE_PATH, 'base')
|
|
|
|
|
|
|
|
class WithCastedModelMixin < Hash
|
|
|
|
include CouchRest::Model::CastedModel
|
|
|
|
property :name
|
|
|
|
property :details, Object, :default => {}
|
|
|
|
property :casted_attribute, WithCastedModelMixin
|
2011-03-03 08:28:57 +01:00
|
|
|
end
|
2011-02-28 16:00:41 +01:00
|
|
|
|
2011-04-20 16:44:49 +02:00
|
|
|
class DirtyModel < CouchRest::Model::Base
|
2011-04-20 12:31:46 +02:00
|
|
|
use_database DB
|
|
|
|
|
2011-02-28 16:00:41 +01:00
|
|
|
property :casted_attribute, WithCastedModelMixin
|
2011-04-20 12:31:46 +02:00
|
|
|
property :title, :default => 'Sample Title'
|
2011-03-03 13:52:19 +01:00
|
|
|
property :details, Object, :default => { 'color' => 'blue' }
|
|
|
|
property :keywords, [String], :default => ['default-keyword']
|
2011-04-20 12:31:46 +02:00
|
|
|
property :sub_models do
|
|
|
|
property :title
|
2011-03-03 08:28:57 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-04-20 16:44:49 +02:00
|
|
|
class DirtyUniqueIdModel < CouchRest::Model::Base
|
|
|
|
use_database DB
|
|
|
|
attr_accessor :code
|
|
|
|
unique_id :code
|
|
|
|
property :title, String, :default => "Sample Title"
|
|
|
|
timestamps!
|
|
|
|
|
|
|
|
def code; self['_id'] || @code; end
|
|
|
|
end
|
|
|
|
|
2011-03-06 03:41:37 +01:00
|
|
|
describe "Dirty" do
|
2011-02-28 16:00:41 +01:00
|
|
|
|
|
|
|
describe "changes" do
|
|
|
|
|
|
|
|
it "should return changes on an attribute" do
|
|
|
|
@card = Card.new(:first_name => "matt")
|
|
|
|
@card.first_name = "andrew"
|
2011-04-20 12:31:46 +02:00
|
|
|
@card.first_name_changed?.should be_true
|
2011-02-28 16:00:41 +01:00
|
|
|
@card.changes.should == { "first_name" => ["matt", "andrew"] }
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "save" do
|
|
|
|
|
|
|
|
it "should not save unchanged records" do
|
|
|
|
card_id = Card.create!(:first_name => "matt").id
|
|
|
|
@card = Card.find(card_id)
|
|
|
|
@card.database.should_not_receive(:save_doc)
|
|
|
|
@card.save
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should save changed records" do
|
|
|
|
card_id = Card.create!(:first_name => "matt").id
|
|
|
|
@card = Card.find(card_id)
|
|
|
|
@card.first_name = "andrew"
|
|
|
|
@card.database.should_receive(:save_doc).and_return({"ok" => true})
|
|
|
|
@card.save
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "changed?" do
|
|
|
|
|
|
|
|
# match activerecord behaviour
|
|
|
|
it "should report no changes on a new object with no attributes set" do
|
|
|
|
@card = Card.new
|
|
|
|
@card.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
2011-03-03 13:52:19 +01:00
|
|
|
it "should report no changes on a hash property with a default value" do
|
2011-04-20 16:44:49 +02:00
|
|
|
@obj = DirtyModel.new
|
2011-03-03 13:52:19 +01:00
|
|
|
@obj.details.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
2011-02-28 16:00:41 +01:00
|
|
|
# match activerecord behaviour
|
|
|
|
it "should report changes on a new object with attributes set" do
|
|
|
|
@card = Card.new(:first_name => "matt")
|
|
|
|
@card.changed?.should be_true
|
|
|
|
end
|
2011-04-20 16:44:49 +02:00
|
|
|
|
|
|
|
it "should report no changes on new object with 'unique_id' set" do
|
|
|
|
@obj = DirtyUniqueIdModel.new
|
|
|
|
@obj.changed?.should be_false
|
|
|
|
@obj.changes.should be_empty
|
|
|
|
end
|
2011-02-28 16:00:41 +01:00
|
|
|
|
|
|
|
it "should report no changes on objects fetched from the database" do
|
|
|
|
card_id = Card.create!(:first_name => "matt").id
|
|
|
|
@card = Card.find(card_id)
|
|
|
|
@card.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if the record is modified" do
|
|
|
|
@card = Card.new
|
|
|
|
@card.first_name = "andrew"
|
|
|
|
@card.changed?.should be_true
|
|
|
|
@card.first_name_changed?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes for unmodified records" do
|
|
|
|
card_id = Card.create!(:first_name => "matt").id
|
|
|
|
@card = Card.find(card_id)
|
|
|
|
@card.first_name = "matt"
|
|
|
|
@card.changed?.should be_false
|
|
|
|
@card.first_name_changed?.should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes after a new record has been saved" do
|
|
|
|
@card = Card.new(:first_name => "matt")
|
|
|
|
@card.save!
|
|
|
|
@card.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes after a record has been saved" do
|
|
|
|
card_id = Card.create!(:first_name => "matt").id
|
|
|
|
@card = Card.find(card_id)
|
|
|
|
@card.first_name = "andrew"
|
|
|
|
@card.save!
|
|
|
|
@card.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
# test changing list properties
|
|
|
|
|
|
|
|
it "should report changes if a list property is modified" do
|
|
|
|
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
|
|
@cat = Cat.find(cat_id)
|
|
|
|
@cat.toys = [{:name => "Feather"}]
|
|
|
|
@cat.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes if a list property is unmodified" do
|
|
|
|
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
|
|
@cat = Cat.find(cat_id)
|
|
|
|
@cat.toys = [{:name => "Mouse"}] # same as original list
|
|
|
|
@cat.changed?.should be_false
|
|
|
|
end
|
|
|
|
|
|
|
|
# attachments
|
|
|
|
|
|
|
|
it "should report changes if an attachment is added" do
|
|
|
|
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
|
|
@file = File.open(FIXTURE_PATH + '/attachments/test.html')
|
|
|
|
@cat = Cat.find(cat_id)
|
|
|
|
@cat.create_attachment(:file => @file, :name => "my_attachment")
|
|
|
|
@cat.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an attachment is deleted" do
|
|
|
|
@cat = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}])
|
|
|
|
@file = File.open(FIXTURE_PATH + '/attachments/test.html')
|
|
|
|
@attachment_name = "my_attachment"
|
|
|
|
@cat.create_attachment(:file => @file, :name => @attachment_name)
|
|
|
|
@cat.save
|
|
|
|
@cat = Cat.find(@cat.id)
|
|
|
|
@cat.delete_attachment(@attachment_name)
|
|
|
|
@cat.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
# casted models
|
|
|
|
|
|
|
|
it "should report changes to casted models" do
|
|
|
|
@cat = Cat.create!(:name => "Felix", :favorite_toy => { :name => "Mouse" })
|
|
|
|
@cat = Cat.find(@cat.id)
|
2011-04-20 16:44:49 +02:00
|
|
|
@cat.favorite_toy.name = 'Feather'
|
2011-02-28 16:00:41 +01:00
|
|
|
@cat.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
2011-04-20 16:44:49 +02:00
|
|
|
it "should report changes to casted model in array" do
|
|
|
|
@obj = Cat.create!(:name => 'felix', :toys => [{:name => "Catnip"}])
|
|
|
|
@obj = Cat.get(@obj.id)
|
|
|
|
@obj.toys.first.name.should eql('Catnip')
|
|
|
|
@obj.toys.first.changed?.should be_false
|
|
|
|
@obj.changed?.should be_false
|
|
|
|
@obj.toys.first.name = "Super Catnip"
|
|
|
|
@obj.toys.first.changed?.should be_true
|
|
|
|
@obj.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes to anonymous casted models in array" do
|
|
|
|
@obj = DirtyModel.create!(:sub_models => [{:title => "Sample"}])
|
|
|
|
@obj = DirtyModel.get(@obj.id)
|
|
|
|
@obj.sub_models.first.title.should eql("Sample")
|
|
|
|
@obj.sub_models.first.changed?.should be_false
|
|
|
|
@obj.changed?.should be_false
|
|
|
|
@obj.sub_models.first.title = "Another Sample"
|
|
|
|
@obj.sub_models.first.changed?.should be_true
|
|
|
|
@obj.changed?.should be_true
|
|
|
|
end
|
|
|
|
|
2011-03-03 13:52:19 +01:00
|
|
|
# casted arrays
|
|
|
|
|
|
|
|
def test_casted_array(change_expected)
|
2011-04-20 16:44:49 +02:00
|
|
|
obj = DirtyModel.create!
|
|
|
|
obj = DirtyModel.get(obj.id)
|
2011-03-03 13:52:19 +01:00
|
|
|
array = obj.keywords
|
|
|
|
yield array, obj
|
|
|
|
if change_expected
|
|
|
|
obj.changed?.should be_true
|
|
|
|
else
|
|
|
|
obj.changed?.should be_false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def should_change_array
|
|
|
|
test_casted_array(true) { |a,b| yield a,b }
|
|
|
|
end
|
|
|
|
|
|
|
|
def should_not_change_array
|
|
|
|
test_casted_array(false) { |a,b| yield a,b }
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array index is modified" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array[0] = "keyword"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes if an array index is unmodified" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array[0] = array[0]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array is appended with <<" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array << 'keyword'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array is popped" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array.pop
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-06-06 01:53:36 +02:00
|
|
|
it "should report changes if an array is popped after reload" do
|
|
|
|
should_change_array do |array, obj|
|
|
|
|
obj.reload
|
|
|
|
obj.keywords.pop
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2011-03-03 13:52:19 +01:00
|
|
|
it "should report no changes if an empty array is popped" do
|
|
|
|
should_not_change_array do |array, obj|
|
|
|
|
array.clear
|
|
|
|
obj.save! # clears changes
|
|
|
|
array.pop
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-06-06 11:55:15 +02:00
|
|
|
it "should report changes on deletion from an array" do
|
|
|
|
should_change_array do |array, obj|
|
|
|
|
array << "keyword"
|
|
|
|
obj.save!
|
|
|
|
array.delete_at(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
should_change_array do |array, obj|
|
|
|
|
array << "keyword"
|
|
|
|
obj.save!
|
|
|
|
array.delete("keyword")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes on deletion from an array after reload" do
|
|
|
|
should_change_array do |array, obj|
|
|
|
|
array << "keyword"
|
|
|
|
obj.save!
|
|
|
|
obj.reload
|
|
|
|
array.delete_at(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
should_change_array do |array, obj|
|
|
|
|
array << "keyword"
|
|
|
|
obj.save!
|
|
|
|
obj.reload
|
|
|
|
array.delete("keyword")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes on deletion from an empty array" do
|
|
|
|
should_not_change_array do |array, obj|
|
|
|
|
array.clear
|
|
|
|
obj.save!
|
|
|
|
array.delete_at(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
should_not_change_array do |array, obj|
|
|
|
|
array.clear
|
|
|
|
obj.save!
|
|
|
|
array.delete("keyword")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-03-03 13:52:19 +01:00
|
|
|
it "should report changes if an array is pushed" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array.push("keyword")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array is shifted" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array.shift
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes if an empty array is shifted" do
|
|
|
|
should_not_change_array do |array, obj|
|
|
|
|
array.clear
|
|
|
|
obj.save! # clears changes
|
|
|
|
array.shift
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array is unshifted" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array.unshift("keyword")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if an array is cleared" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_array do |array, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
array.clear
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Object, {} (casted hash)
|
|
|
|
|
|
|
|
def test_casted_hash(change_expected)
|
2011-04-20 16:44:49 +02:00
|
|
|
obj = DirtyModel.create!
|
|
|
|
obj = DirtyModel.get(obj.id)
|
2011-03-03 13:52:19 +01:00
|
|
|
hash = obj.details
|
|
|
|
yield hash, obj
|
|
|
|
if change_expected
|
|
|
|
obj.changed?.should be_true
|
|
|
|
else
|
|
|
|
obj.changed?.should be_false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def should_change_hash
|
|
|
|
test_casted_hash(true) { |a,b| yield a,b }
|
|
|
|
end
|
|
|
|
|
|
|
|
def should_not_change_hash
|
|
|
|
test_casted_hash(false) { |a,b| yield a,b }
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes if a hash is modified" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash['color'] = 'orange'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes if a hash is unmodified" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash['color'] = hash['color']
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes when deleting from a hash" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.delete('color')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes when deleting a non existent key from a hash" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.delete('non-existent-key')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes when clearing a hash" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.clear
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes when merging changes to a hash" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.merge!('foo' => 'bar')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes when merging no changes to a hash" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.merge!('color' => hash['color'])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes when replacing hash content" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.replace('foo' => 'bar')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes when replacing hash content with same content" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.replace(hash)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report changes when removing records with delete_if" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.delete_if { true }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should report no changes when removing no records with delete_if" do
|
2011-03-06 00:28:54 +01:00
|
|
|
should_not_change_hash do |hash, obj|
|
2011-03-03 13:52:19 +01:00
|
|
|
hash.delete_if { false }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-03-06 00:28:54 +01:00
|
|
|
if {}.respond_to?(:keep_if)
|
|
|
|
|
|
|
|
it "should report changes when removing records with keep_if" do
|
|
|
|
should_change_hash do |hash, obj|
|
|
|
|
hash.keep_if { false }
|
|
|
|
end
|
2011-03-03 13:52:19 +01:00
|
|
|
end
|
|
|
|
|
2011-03-06 00:28:54 +01:00
|
|
|
it "should report no changes when removing no records with keep_if" do
|
|
|
|
should_not_change_hash do |hash, obj|
|
|
|
|
hash.keep_if { true }
|
|
|
|
end
|
2011-03-03 13:52:19 +01:00
|
|
|
end
|
2011-03-06 00:28:54 +01:00
|
|
|
|
2011-02-28 16:00:41 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|