implemented some missing dirty functionality for casted_array and casted_hash. improved dirty spec test
This commit is contained in:
parent
dcf43e3641
commit
2a9305ebd3
|
@ -33,6 +33,10 @@ def set_dirty(value)
|
||||||
orig
|
orig
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def supports_dirty?
|
||||||
|
CouchRest::Model::Base.respond_to?(:use_dirty)
|
||||||
|
end
|
||||||
|
|
||||||
def run_benchmark
|
def run_benchmark
|
||||||
n = 50000 # property operation count
|
n = 50000 # property operation count
|
||||||
db_n = 1000 # database operation count
|
db_n = 1000 # database operation count
|
||||||
|
@ -97,11 +101,13 @@ def run_benchmark
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
puts "with use_dirty true"
|
if supports_dirty?
|
||||||
set_dirty(true)
|
puts "with use_dirty true"
|
||||||
run_benchmark
|
set_dirty(true)
|
||||||
|
run_benchmark
|
||||||
|
|
||||||
puts "\nwith use_dirty false"
|
puts "\nwith use_dirty false"
|
||||||
set_dirty(false)
|
set_dirty(false)
|
||||||
|
end
|
||||||
run_benchmark
|
run_benchmark
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,36 +15,41 @@ module CouchRest::Model
|
||||||
end
|
end
|
||||||
|
|
||||||
def << obj
|
def << obj
|
||||||
couchrest_parent_will_change!
|
couchrest_parent_will_change! if use_dirty?
|
||||||
super(instantiate_and_cast(obj))
|
super(instantiate_and_cast(obj))
|
||||||
end
|
end
|
||||||
|
|
||||||
def push(obj)
|
def push(obj)
|
||||||
couchrest_parent_will_change!
|
couchrest_parent_will_change! if use_dirty?
|
||||||
super(instantiate_and_cast(obj))
|
super(instantiate_and_cast(obj))
|
||||||
end
|
end
|
||||||
|
|
||||||
def pop
|
def pop
|
||||||
couchrest_parent_will_change!
|
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def shift
|
def shift
|
||||||
couchrest_parent_will_change!
|
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def unshift(obj)
|
def unshift(obj)
|
||||||
couchrest_parent_will_change!
|
couchrest_parent_will_change! if use_dirty?
|
||||||
super(obj)
|
super(instantiate_and_cast(obj))
|
||||||
end
|
end
|
||||||
|
|
||||||
def []= index, obj
|
def []= index, obj
|
||||||
value = instantiate_and_cast(obj)
|
value = instantiate_and_cast(obj)
|
||||||
couchrest_parent_will_change! if value != self[index]
|
couchrest_parent_will_change! if use_dirty? && value != self[index]
|
||||||
super(index, value)
|
super(index, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def instantiate_and_cast(obj)
|
def instantiate_and_cast(obj)
|
||||||
|
|
|
@ -6,16 +6,70 @@ module CouchRest::Model
|
||||||
include CouchRest::Model::Dirty
|
include CouchRest::Model::Dirty
|
||||||
attr_accessor :casted_by
|
attr_accessor :casted_by
|
||||||
|
|
||||||
def []= index, obj
|
|
||||||
return super(index, obj) unless use_dirty?
|
|
||||||
couchrest_parent_will_change! if obj != self[index]
|
|
||||||
super(index, obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
# needed for dirty
|
# needed for dirty
|
||||||
def attributes
|
def attributes
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def []= key, obj
|
||||||
|
couchrest_attribute_will_change!(key) if use_dirty? && obj != self[key]
|
||||||
|
super(key, obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
couchrest_attribute_will_change!(key) if use_dirty? && include?(key)
|
||||||
|
super(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge!(other_hash)
|
||||||
|
if use_dirty? && other_hash && other_hash.kind_of?(Hash)
|
||||||
|
other_hash.keys.each do |key|
|
||||||
|
if self[key] != other_hash[key] || !include?(key)
|
||||||
|
couchrest_attribute_will_change!(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super(other_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace(other_hash)
|
||||||
|
if use_dirty? && other_hash && other_hash.kind_of?(Hash)
|
||||||
|
# new keys and changed keys
|
||||||
|
other_hash.keys.each do |key|
|
||||||
|
if self[key] != other_hash[key] || !include?(key)
|
||||||
|
couchrest_attribute_will_change!(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# old keys
|
||||||
|
old_keys = self.keys.reject { |key| other_hash.include?(key) }
|
||||||
|
old_keys.each { |key| couchrest_attribute_will_change!(key) }
|
||||||
|
end
|
||||||
|
|
||||||
|
super(other_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
self.keys.each { |key| couchrest_attribute_will_change!(key) } if use_dirty?
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_if
|
||||||
|
if use_dirty? && block_given?
|
||||||
|
self.keys.each do |key|
|
||||||
|
couchrest_attribute_will_change!(key) if yield key, self[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def keep_if
|
||||||
|
if use_dirty? && block_given?
|
||||||
|
self.keys.each do |key|
|
||||||
|
couchrest_attribute_will_change!(key) if !yield key, self[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,11 +13,6 @@ module CouchRest
|
||||||
include CouchRest::Model::CastedBy # needed for base_doc
|
include CouchRest::Model::CastedBy # needed for base_doc
|
||||||
include ActiveModel::Dirty
|
include ActiveModel::Dirty
|
||||||
|
|
||||||
def use_dirty?
|
|
||||||
bdoc = base_doc
|
|
||||||
bdoc && !bdoc.disable_dirty && bdoc.use_dirty
|
|
||||||
end
|
|
||||||
|
|
||||||
included do
|
included do
|
||||||
# internal dirty setting - overrides global setting.
|
# internal dirty setting - overrides global setting.
|
||||||
# this is used to temporarily disable dirty tracking when setting
|
# this is used to temporarily disable dirty tracking when setting
|
||||||
|
@ -25,6 +20,11 @@ module CouchRest
|
||||||
self.send(:attr_accessor, :disable_dirty)
|
self.send(:attr_accessor, :disable_dirty)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def use_dirty?
|
||||||
|
bdoc = base_doc
|
||||||
|
bdoc && !bdoc.disable_dirty && bdoc.use_dirty
|
||||||
|
end
|
||||||
|
|
||||||
def couchrest_attribute_will_change!(attr)
|
def couchrest_attribute_will_change!(attr)
|
||||||
return if attr.nil? || !use_dirty?
|
return if attr.nil? || !use_dirty?
|
||||||
attribute_will_change!(attr)
|
attribute_will_change!(attr)
|
||||||
|
|
|
@ -201,7 +201,7 @@ module CouchRest
|
||||||
end
|
end
|
||||||
type = [type] # inject as an array
|
type = [type] # inject as an array
|
||||||
end
|
end
|
||||||
property = Property.new(name, type, options)
|
property = Property.new(name, type, options.merge(:use_dirty => use_dirty))
|
||||||
create_property_getter(property)
|
create_property_getter(property)
|
||||||
create_property_setter(property) unless property.read_only == true
|
create_property_setter(property) unless property.read_only == true
|
||||||
if property.type_class.respond_to?(:validates_casted_model)
|
if property.type_class.respond_to?(:validates_casted_model)
|
||||||
|
|
|
@ -4,7 +4,7 @@ module CouchRest::Model
|
||||||
|
|
||||||
include ::CouchRest::Model::Typecast
|
include ::CouchRest::Model::Typecast
|
||||||
|
|
||||||
attr_reader :name, :type, :type_class, :read_only, :alias, :default, :casted, :init_method, :options
|
attr_reader :name, :type, :type_class, :read_only, :alias, :default, :casted, :init_method, :use_dirty, :options
|
||||||
|
|
||||||
# Attribute to define.
|
# Attribute to define.
|
||||||
# All Properties are assumed casted unless the type is nil.
|
# All Properties are assumed casted unless the type is nil.
|
||||||
|
@ -38,7 +38,7 @@ module CouchRest::Model
|
||||||
end
|
end
|
||||||
arr = value.collect { |data| cast_value(parent, data) }
|
arr = value.collect { |data| cast_value(parent, data) }
|
||||||
# allow casted_by calls to be passed up chain by wrapping in CastedArray
|
# allow casted_by calls to be passed up chain by wrapping in CastedArray
|
||||||
value = type_class != String ? CastedArray.new(arr, self) : arr
|
value = (use_dirty || type_class != String) ? CastedArray.new(arr, self) : arr
|
||||||
value.casted_by = parent if value.respond_to?(:casted_by)
|
value.casted_by = parent if value.respond_to?(:casted_by)
|
||||||
elsif (type == Object || type == Hash) && (value.class == Hash)
|
elsif (type == Object || type == Hash) && (value.class == Hash)
|
||||||
# allow casted_by calls to be passed up chain by wrapping in CastedHash
|
# allow casted_by calls to be passed up chain by wrapping in CastedHash
|
||||||
|
@ -94,6 +94,7 @@ module CouchRest::Model
|
||||||
@alias = options.delete(:alias) if options[:alias]
|
@alias = options.delete(:alias) if options[:alias]
|
||||||
@default = options.delete(:default) unless options[:default].nil?
|
@default = options.delete(:default) unless options[:default].nil?
|
||||||
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
||||||
|
@use_dirty = options.delete(:use_dirty)
|
||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ describe CouchRest::Model::CastedModel do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should cast the array properly" do
|
it "should cast the array properly" do
|
||||||
@obj.keywords.should be_an_instance_of(Array)
|
@obj.keywords.should be_kind_of(Array)
|
||||||
@obj.keywords.first.should == 'couch'
|
@obj.keywords.first.should == 'couch'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,8 +17,8 @@ class DummyModel < CouchRest::Model::Base
|
||||||
use_database TEST_SERVER.default_database
|
use_database TEST_SERVER.default_database
|
||||||
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
||||||
property :casted_attribute, WithCastedModelMixin
|
property :casted_attribute, WithCastedModelMixin
|
||||||
property :details, Object, :default => {}
|
property :details, Object, :default => { 'color' => 'blue' }
|
||||||
property :keywords, [String]
|
property :keywords, [String], :default => ['default-keyword']
|
||||||
property :sub_models do |child|
|
property :sub_models do |child|
|
||||||
child.property :title
|
child.property :title
|
||||||
end
|
end
|
||||||
|
@ -128,6 +128,11 @@ describe "With use_dirty(on)" do
|
||||||
@card.changed?.should be_false
|
@card.changed?.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should report no changes on a hash property with a default value" do
|
||||||
|
@obj = DummyModel.new
|
||||||
|
@obj.details.changed?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
# match activerecord behaviour
|
# match activerecord behaviour
|
||||||
# not currently working - not too important
|
# not currently working - not too important
|
||||||
|
@ -218,12 +223,190 @@ describe "With use_dirty(on)" do
|
||||||
@cat.changed?.should be_true
|
@cat.changed?.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should report changes to hashes" do
|
# casted arrays
|
||||||
@obj = DummyModel.create!
|
|
||||||
@obj = DummyModel.get(@obj.id)
|
def test_casted_array(change_expected)
|
||||||
deets = @obj.details
|
obj = DummyModel.create!
|
||||||
deets['color'] = 'orange'
|
obj = DummyModel.get(obj.id)
|
||||||
@obj.changed?.should be_true
|
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
|
||||||
|
should_change_array do |array|
|
||||||
|
array[0] = "keyword"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes if an array index is unmodified" do
|
||||||
|
should_not_change_array do |array|
|
||||||
|
array[0] = array[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes if an array is appended with <<" do
|
||||||
|
should_change_array do |array|
|
||||||
|
array << 'keyword'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes if an array is popped" do
|
||||||
|
should_change_array do |array|
|
||||||
|
array.pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
it "should report changes if an array is pushed" do
|
||||||
|
should_change_array do |array|
|
||||||
|
array.push("keyword")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes if an array is shifted" do
|
||||||
|
should_change_array do |array|
|
||||||
|
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
|
||||||
|
should_change_array do |array|
|
||||||
|
array.unshift("keyword")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes if an array is cleared" do
|
||||||
|
should_change_array do |array|
|
||||||
|
array.clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Object, {} (casted hash)
|
||||||
|
|
||||||
|
def test_casted_hash(change_expected)
|
||||||
|
obj = DummyModel.create!
|
||||||
|
obj = DummyModel.get(obj.id)
|
||||||
|
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
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash['color'] = 'orange'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes if a hash is unmodified" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash['color'] = hash['color']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when deleting from a hash" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.delete('color')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes when deleting a non existent key from a hash" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash.delete('non-existent-key')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when clearing a hash" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when merging changes to a hash" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.merge!('foo' => 'bar')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes when merging no changes to a hash" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash.merge!('color' => hash['color'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when replacing hash content" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.replace('foo' => 'bar')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes when replacing hash content with same content" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash.replace(hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when removing records with delete_if" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.delete_if { true }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes when removing no records with delete_if" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash.delete_if { false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report changes when removing records with keep_if" do
|
||||||
|
should_change_hash do |hash|
|
||||||
|
hash.keep_if { false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should report no changes when removing no records with keep_if" do
|
||||||
|
should_not_change_hash do |hash|
|
||||||
|
hash.keep_if { true }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue