Permalink
Browse files

Save typecast/uncast values and use those where possible, rather than…

… recomputing them every time they're accessed.

Use #key? rather than #keys.include? for better performance

Significantly reduce calls to #read_keys by avoiding #attributes calls from ActiveModel::Dirty. Improve the speed of #read_keys by avoiding string interns and ivar lookups where possible. Eliminate extraneous array creations in Keys#attributes and Associations#embedded_associations. Improve the speed of Dirty#write_key by using ivars directly rather than going through instance methods.

Revert to the slower-but-more-semantic key reads for #attributes now that the Dirty attributes thrashing is fixed.

attr_accessor -> attr_reader for semantic correctness
  • Loading branch information...
1 parent 400f4b3 commit 942003cca2c2daddac8615452fdf93387eb49108 @cheald cheald committed Oct 21, 2012
View
@@ -27,9 +27,14 @@ def associations
end
def associations=(hash)
+ @embedded_associations = nil
@associations = hash
end
+ def embedded_associations
+ @embedded_associations ||= associations.values.select { |assoc| assoc.embeddable? }
+ end
+
def associations_module_defined?
if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
const_defined?('MongoMapperAssociations')
@@ -51,6 +56,7 @@ def associations_module
private
def create_association(association)
+ @embedded_associations = nil
associations[association.name] = association
association.setup(self)
end
@@ -61,7 +67,7 @@ def associations
end
def embedded_associations
- associations.values.select { |assoc| assoc.embeddable? }
+ self.class.embedded_associations
end
def build_proxy(association)
View
4 lib/mongo_mapper/plugins/clone.rb 100644 → 100755
@@ -7,7 +7,9 @@ module Clone
def initialize_copy(other)
@_new = true
@_destroyed = false
- @_id = nil
+ remove_instance_variable :@_id
+ remove_instance_variable :@_id_before_type_cast
+
associations.each do |name, association|
instance_variable_set(association.ivar, nil)
end
@@ -25,11 +25,9 @@ def reload(*)
protected
- def attribute_method?(attr)
- # This overrides ::ActiveSupport::Dirty#attribute_method? to allow attributes to be any key
- # in the attributes hash ( default ) or any key defined on the model that may not yet have
- # had a value stored in the attributes collection.
- super || key_names.include?(attr)
+ # We don't call super here to avoid invoking #attributes, which builds a whole new hash per call.
+ def attribute_method?(attr_name)
+ keys.key?(attr_name) || !embedded_associations.detect {|a| a.name == attr_name }.nil?
end
def clear_changes
@@ -53,7 +51,7 @@ def write_key(key, value)
end
def attribute_value_changed?(key_name)
- attribute_was(key_name) != read_key(key_name)
+ changed_attributes[key_name] != instance_variable_get(:"@#{key_name}")
end
end
end
View
@@ -23,7 +23,7 @@ def reload
self.class.associations.each_value do |association|
get_proxy(association).reset
end
- instance_variables.each { |ivar| instance_variable_set(ivar, nil) }
+ instance_variables.each { |ivar| remove_instance_variable(ivar) }
load_from_database(doc)
self
else
@@ -61,7 +61,7 @@ def from_mongo(value)
def load(attrs)
return nil if attrs.nil?
begin
- attrs['_type'].present? ? attrs['_type'].constantize : self
+ attrs['_type'] ? attrs['_type'].constantize : self
rescue NameError
self
end.allocate.initialize_from_database(attrs)
@@ -86,14 +86,12 @@ def accessors_module
def create_accessors_for(key)
accessors_module.module_eval <<-end_eval
+ attr_reader :#{key.name}_before_type_cast
+
def #{key.name}
read_key(:#{key.name})
end
- def #{key.name}_before_type_cast
- read_key_before_type_cast(:#{key.name})
- end
-
def #{key.name}=(value)
write_key(:#{key.name}, value)
end
@@ -194,9 +192,11 @@ def attributes=(attrs)
def attributes
HashWithIndifferentAccess.new.tap do |attrs|
- keys.select { |name,key| !self[key.name].nil? || key.type == ObjectId }.each do |name, key|
- value = key.set(self[key.name])
- attrs[name] = value
+ keys.each do |name, key|
+ if key.type == ObjectId || !self[key.name].nil?
+ value = key.set(self[key.name])
+ attrs[name] = value
+ end
end
embedded_associations.each do |association|
@@ -245,10 +245,12 @@ def id=(value)
end
def read_key(key_name)
- if key = keys[key_name.to_s]
- value = key.get(instance_variable_get(:"@#{key_name}"))
- set_parent_document(key, value) if key.embeddable?
- instance_variable_set(:"@#{key_name}", value)
+ instance_key = :"@#{key_name}"
+ if instance_variable_defined? instance_key
+ instance_variable_get instance_key
+ elsif key = keys[key_name.to_s]
+ value = key.get instance_variable_get(:"@#{key_name}_before_type_cast")
+ instance_variable_set instance_key, value
end
end
@@ -287,32 +289,34 @@ def load_from_database(attrs)
end
end
+
def ensure_key_exists(name)
self.class.key(name) unless respond_to?(:"#{name}=")
end
def set_parent_document(key, value)
- if key.embeddable? && value.is_a?(key.type)
+ if value.respond_to?(:_parent_document) && value.is_a?(key.type) && key.embeddable?
value._parent_document = self
end
end
- def read_key_before_type_cast(name)
- instance_variable_get(:"@#{name}_before_type_cast")
- end
-
def write_key(name, value)
- key = keys[name.to_s]
- set_parent_document(key, value) if key.embeddable?
- instance_variable_set :"@#{name}_before_type_cast", value
- instance_variable_set :"@#{name}", key.set(value)
+ key = keys[name.to_s]
+ as_mongo = key.set(value)
+ as_typecast = key.get(as_mongo)
+ set_parent_document(key, value)
+ set_parent_document(key, as_typecast)
+ instance_variable_set :"@#{key.name}", as_typecast
+ instance_variable_set :"@#{key.name}_before_type_cast", value
+ @attributes = nil
end
def initialize_default_values
keys.values.select { |key| key.default? }.each do |key|
write_key key.name, key.default_value
end
end
+ #end private
end
end
end
View
2 lib/mongo_mapper/plugins/rails.rb 100644 → 100755
@@ -26,7 +26,7 @@ def read_attribute(name)
end
def read_attribute_before_type_cast(name)
- read_key_before_type_cast(name)
+ send "#{name}_before_type_cast"
end
def write_attribute(name, value)
View
No changes.
View
@@ -493,7 +493,7 @@ def name_and_age
should "be accessible for use in a document" do
@document.class_eval do
def untypcasted_name
- read_key_before_type_cast(:name)
+ @name_before_type_cast
end
end

0 comments on commit 942003c

Please sign in to comment.