some speed optimisations. added 'use_dirty' configuration variable

This commit is contained in:
Andrew Williams 2011-03-03 17:58:57 +10:30
parent ce2e2fc9a6
commit dcf43e3641
13 changed files with 184 additions and 69 deletions

View file

@ -17,6 +17,7 @@ module CouchRest
include CouchRest::Model::Associations
include CouchRest::Model::Validations
include CouchRest::Model::Dirty
include CouchRest::Model::CastedBy
def self.subclasses
@subclasses ||= []
@ -25,6 +26,7 @@ module CouchRest
def self.inherited(subklass)
super
subklass.send(:include, CouchRest::Model::Properties)
subklass.class_eval <<-EOS, __FILE__, __LINE__ + 1
def self.inherited(subklass)
super
@ -72,16 +74,9 @@ module CouchRest
end
### instance methods
# Gets a reference to the actual document in the DB
# Calls up to the next document if there is one,
# Otherwise we're at the top and we return self
def base_doc
return self if base_doc?
@casted_by.base_doc
end
# Checks if we're the top document
# (overrides base_doc? in casted_by.rb)
def base_doc?
!@casted_by
end

View file

@ -0,0 +1,23 @@
module CouchRest::Model
module CastedBy
extend ActiveSupport::Concern
included do
self.send(:attr_accessor, :casted_by)
end
# Gets a reference to the actual document in the DB
# Calls up to the next document if there is one,
# Otherwise we're at the top and we return self
def base_doc
return self if base_doc?
@casted_by ? @casted_by.base_doc : nil
end
# Checks if we're the top document
def base_doc?
false
end
end
end

View file

@ -7,6 +7,7 @@ module CouchRest::Model
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

View file

@ -21,7 +21,7 @@ module CouchRest::Model
end
def []= key, value
couchrest_attribute_will_change!(key) unless self[key] == value
couchrest_attribute_will_change!(key) if use_dirty && self[key] != value
super(key.to_s, value)
end

View file

@ -10,10 +10,12 @@ module CouchRest
included do
add_config :model_type_key
add_config :mass_assign_any_attribute
add_config :use_dirty
configure do |config|
config.model_type_key = 'couchrest-type' # 'model'?
config.mass_assign_any_attribute = false
config.use_dirty = true
end
end

View file

@ -10,14 +10,24 @@ module CouchRest
# This applies to both Model::Base and Model::CastedModel
module Dirty
extend ActiveSupport::Concern
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
include ActiveModel::Dirty
# internal dirty setting - overrides global setting.
# this is used to temporarily disable dirty tracking when setting
# attributes directly, for performance reasons.
self.send(:attr_accessor, :disable_dirty)
end
def couchrest_attribute_will_change!(attr)
return if attr.nil?
self.send("#{attr}_will_change!")
return if attr.nil? || !use_dirty?
attribute_will_change!(attr)
couchrest_parent_will_change!
end
@ -29,7 +39,7 @@ module CouchRest
# return the attribute name this object is referenced by in the parent
def casted_by_attribute
return @casted_by_attribute if @casted_by_attribute_set
return @casted_by_attribute if @casted_by_attribute
attr = @casted_by.attributes
@casted_by_attribute = attr.keys.detect { |k| attr[k] == self }
end

View file

@ -2,11 +2,6 @@ module CouchRest
module Model
module ExtendedAttachments
extend ActiveSupport::Concern
include ActiveModel::Dirty
included do
# for _attachments_will_change!
define_attribute_methods [:_attachments]
end
# Add a file attachment to the current document. Expects
# :file and :name to be included in the arguments.
@ -41,8 +36,10 @@ module CouchRest
# deletes a file attachment from the current doc
def delete_attachment(attachment_name)
return unless attachments
_attachments_will_change! if attachments.include?(attachment_name)
attachments.delete attachment_name
if attachments.include?(attachment_name)
attribute_will_change!("_attachments")
attachments.delete attachment_name
end
end
# returns true if attachment_name exists
@ -74,7 +71,7 @@ module CouchRest
content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file].path)
content_type ||= (get_mime_type(args[:name]) || 'text/plain')
_attachments_will_change!
attribute_will_change!("_attachments")
attachments[args[:name]] = {
'content_type' => content_type,
'data' => args[:file].read

View file

@ -30,7 +30,7 @@ module CouchRest
def update(options = {})
raise "Calling #{self.class.name}#update on document that has not been created!" if self.new?
return false unless perform_validations(options)
return true unless self.changed?
return true if use_dirty? && !self.changed?
_run_update_callbacks do
_run_save_callbacks do
result = database.save_doc(self)

View file

@ -3,7 +3,6 @@ module CouchRest
module Model
module Properties
extend ActiveSupport::Concern
include ActiveModel::Dirty
included do
extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties)
@ -48,11 +47,14 @@ module CouchRest
def write_attribute_dirty(property, value)
prop = find_property!(property)
value = prop.is_a?(String) ? value : prop.cast(self, value)
self.send("#{prop}_will_change!") unless self[prop.to_s] == value
write_attribute(property, value)
propname = prop.to_s
attribute_will_change!(propname) if use_dirty? && self[propname] != value
self[propname] = value
end
def []=(key,value)
return super(key,value) unless use_dirty?
has_changes = self.changed?
if !has_changes && self.respond_to?(:get_unique_id)
check_id_change = true
@ -82,14 +84,8 @@ module CouchRest
end
alias :attributes= :update_attributes_without_saving
# needed for Dirty
def attributes
ret = {}
self.class.properties.each do |property|
ret[property.name] = read_attribute(property)
end
ret
end
# 'attributes' needed for Dirty
alias :attributes :properties_with_values
def find_property(property)
property.is_a?(Property) ? property : self.class.properties.detect {|p| p.to_s == property.to_s}
@ -123,18 +119,10 @@ module CouchRest
# Set all the attributes and return a hash with the attributes
# that have not been accepted.
def directly_set_attributes(hash, options = {})
hash.reject do |attribute_name, attribute_value|
self.disable_dirty = !options[:dirty]
ret = hash.reject do |attribute_name, attribute_value|
if self.respond_to?("#{attribute_name}=")
if find_property(attribute_name)
if options[:dirty]
self.write_attribute_dirty(attribute_name, attribute_value)
else
# set attribute without updating dirty status
self.write_attribute(attribute_name, attribute_value)
end
else
self.send("#{attribute_name}=", attribute_value)
end
self.send("#{attribute_name}=", attribute_value)
true
elsif mass_assign_any_attribute # config option
self[attribute_name] = attribute_value
@ -143,6 +131,8 @@ module CouchRest
false
end
end
self.disable_dirty = false
ret
end
def directly_set_read_only_attributes(hash)