implemented some missing dirty functionality for casted_array and casted_hash. improved dirty spec test
This commit is contained in:
parent
dcf43e3641
commit
2a9305ebd3
8 changed files with 284 additions and 35 deletions
|
@ -33,6 +33,10 @@ def set_dirty(value)
|
|||
orig
|
||||
end
|
||||
|
||||
def supports_dirty?
|
||||
CouchRest::Model::Base.respond_to?(:use_dirty)
|
||||
end
|
||||
|
||||
def run_benchmark
|
||||
n = 50000 # property operation count
|
||||
db_n = 1000 # database operation count
|
||||
|
@ -97,11 +101,13 @@ def run_benchmark
|
|||
end
|
||||
|
||||
begin
|
||||
puts "with use_dirty true"
|
||||
set_dirty(true)
|
||||
run_benchmark
|
||||
if supports_dirty?
|
||||
puts "with use_dirty true"
|
||||
set_dirty(true)
|
||||
run_benchmark
|
||||
|
||||
puts "\nwith use_dirty false"
|
||||
set_dirty(false)
|
||||
puts "\nwith use_dirty false"
|
||||
set_dirty(false)
|
||||
end
|
||||
run_benchmark
|
||||
end
|
||||
|
|
|
@ -15,36 +15,41 @@ module CouchRest::Model
|
|||
end
|
||||
|
||||
def << obj
|
||||
couchrest_parent_will_change!
|
||||
couchrest_parent_will_change! if use_dirty?
|
||||
super(instantiate_and_cast(obj))
|
||||
end
|
||||
|
||||
def push(obj)
|
||||
couchrest_parent_will_change!
|
||||
couchrest_parent_will_change! if use_dirty?
|
||||
super(instantiate_and_cast(obj))
|
||||
end
|
||||
|
||||
def pop
|
||||
couchrest_parent_will_change!
|
||||
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||
super
|
||||
end
|
||||
|
||||
def shift
|
||||
couchrest_parent_will_change!
|
||||
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||
super
|
||||
end
|
||||
|
||||
def unshift(obj)
|
||||
couchrest_parent_will_change!
|
||||
super(obj)
|
||||
couchrest_parent_will_change! if use_dirty?
|
||||
super(instantiate_and_cast(obj))
|
||||
end
|
||||
|
||||
def []= index, 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)
|
||||
end
|
||||
|
||||
def clear
|
||||
couchrest_parent_will_change! if use_dirty? && self.length > 0
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def instantiate_and_cast(obj)
|
||||
|
|
|
@ -6,16 +6,70 @@ module CouchRest::Model
|
|||
include CouchRest::Model::Dirty
|
||||
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
|
||||
def attributes
|
||||
self
|
||||
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
|
||||
|
|
|
@ -13,11 +13,6 @@ module CouchRest
|
|||
include CouchRest::Model::CastedBy # needed for base_doc
|
||||
include ActiveModel::Dirty
|
||||
|
||||
def use_dirty?
|
||||
bdoc = base_doc
|
||||
bdoc && !bdoc.disable_dirty && bdoc.use_dirty
|
||||
end
|
||||
|
||||
included do
|
||||
# internal dirty setting - overrides global setting.
|
||||
# this is used to temporarily disable dirty tracking when setting
|
||||
|
@ -25,6 +20,11 @@ module CouchRest
|
|||
self.send(:attr_accessor, :disable_dirty)
|
||||
end
|
||||
|
||||
def use_dirty?
|
||||
bdoc = base_doc
|
||||
bdoc && !bdoc.disable_dirty && bdoc.use_dirty
|
||||
end
|
||||
|
||||
def couchrest_attribute_will_change!(attr)
|
||||
return if attr.nil? || !use_dirty?
|
||||
attribute_will_change!(attr)
|
||||
|
|
|
@ -201,7 +201,7 @@ module CouchRest
|
|||
end
|
||||
type = [type] # inject as an array
|
||||
end
|
||||
property = Property.new(name, type, options)
|
||||
property = Property.new(name, type, options.merge(:use_dirty => use_dirty))
|
||||
create_property_getter(property)
|
||||
create_property_setter(property) unless property.read_only == true
|
||||
if property.type_class.respond_to?(:validates_casted_model)
|
||||
|
|
|
@ -4,7 +4,7 @@ module CouchRest::Model
|
|||
|
||||
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.
|
||||
# All Properties are assumed casted unless the type is nil.
|
||||
|
@ -38,7 +38,7 @@ module CouchRest::Model
|
|||
end
|
||||
arr = value.collect { |data| cast_value(parent, data) }
|
||||
# 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)
|
||||
elsif (type == Object || type == Hash) && (value.class == Hash)
|
||||
# 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]
|
||||
@default = options.delete(:default) unless options[:default].nil?
|
||||
@init_method = options[:init_method] ? options.delete(:init_method) : 'new'
|
||||
@use_dirty = options.delete(:use_dirty)
|
||||
@options = options
|
||||
end
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ describe CouchRest::Model::CastedModel do
|
|||
end
|
||||
|
||||
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'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,8 +17,8 @@ class DummyModel < CouchRest::Model::Base
|
|||
use_database TEST_SERVER.default_database
|
||||
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
||||
property :casted_attribute, WithCastedModelMixin
|
||||
property :details, Object, :default => {}
|
||||
property :keywords, [String]
|
||||
property :details, Object, :default => { 'color' => 'blue' }
|
||||
property :keywords, [String], :default => ['default-keyword']
|
||||
property :sub_models do |child|
|
||||
child.property :title
|
||||
end
|
||||
|
@ -128,6 +128,11 @@ describe "With use_dirty(on)" do
|
|||
@card.changed?.should be_false
|
||||
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
|
||||
# match activerecord behaviour
|
||||
# not currently working - not too important
|
||||
|
@ -218,12 +223,190 @@ describe "With use_dirty(on)" do
|
|||
@cat.changed?.should be_true
|
||||
end
|
||||
|
||||
it "should report changes to hashes" do
|
||||
@obj = DummyModel.create!
|
||||
@obj = DummyModel.get(@obj.id)
|
||||
deets = @obj.details
|
||||
deets['color'] = 'orange'
|
||||
@obj.changed?.should be_true
|
||||
# casted arrays
|
||||
|
||||
def test_casted_array(change_expected)
|
||||
obj = DummyModel.create!
|
||||
obj = DummyModel.get(obj.id)
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue