Improve the performance of reading attributes
We added a comparison to "id", and call to `self.class.primary_key` a *lot*. We also have performance hits from `&block` all over the place. We skip the check in a new method, in order to avoid breaking the behavior of `read_attribute`
This commit is contained in:
parent
78e7a0d3b7
commit
08576b94ad
@ -230,8 +230,8 @@ def composed_of(part_id, options = {})
|
|||||||
private
|
private
|
||||||
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
||||||
define_method(name) do
|
define_method(name) do
|
||||||
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !read_attribute(key).nil? })
|
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
|
||||||
attrs = mapping.collect {|key, _| read_attribute(key)}
|
attrs = mapping.collect {|key, _| _read_attribute(key)}
|
||||||
object = constructor.respond_to?(:call) ?
|
object = constructor.respond_to?(:call) ?
|
||||||
constructor.call(*attrs) :
|
constructor.call(*attrs) :
|
||||||
class_name.constantize.send(constructor, *attrs)
|
class_name.constantize.send(constructor, *attrs)
|
||||||
|
@ -66,7 +66,7 @@ def empty?
|
|||||||
# the loaded flag is set to true as well.
|
# the loaded flag is set to true as well.
|
||||||
def count_records
|
def count_records
|
||||||
count = if has_cached_counter?
|
count = if has_cached_counter?
|
||||||
owner.read_attribute cached_counter_attribute_name
|
owner._read_attribute cached_counter_attribute_name
|
||||||
else
|
else
|
||||||
scope.count
|
scope.count
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ def initialize(owner, reflection)
|
|||||||
# SELECT query will be generated by using #length instead.
|
# SELECT query will be generated by using #length instead.
|
||||||
def size
|
def size
|
||||||
if has_cached_counter?
|
if has_cached_counter?
|
||||||
owner.read_attribute cached_counter_attribute_name(reflection)
|
owner._read_attribute cached_counter_attribute_name(reflection)
|
||||||
elsif loaded?
|
elsif loaded?
|
||||||
target.size
|
target.size
|
||||||
else
|
else
|
||||||
|
@ -332,7 +332,7 @@ def attribute_for_inspect(attr_name)
|
|||||||
# task.attribute_present?(:title) # => true
|
# task.attribute_present?(:title) # => true
|
||||||
# task.attribute_present?(:is_done) # => true
|
# task.attribute_present?(:is_done) # => true
|
||||||
def attribute_present?(attribute)
|
def attribute_present?(attribute)
|
||||||
value = read_attribute(attribute)
|
value = _read_attribute(attribute)
|
||||||
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ def pk_attribute?(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def typecasted_attribute_value(name)
|
def typecasted_attribute_value(name)
|
||||||
read_attribute(name)
|
_read_attribute(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -110,7 +110,7 @@ def old_attribute_value(attr)
|
|||||||
if attribute_changed?(attr)
|
if attribute_changed?(attr)
|
||||||
changed_attributes[attr]
|
changed_attributes[attr]
|
||||||
else
|
else
|
||||||
clone_attribute_value(:read_attribute, attr)
|
clone_attribute_value(:_read_attribute, attr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ def to_key
|
|||||||
def id
|
def id
|
||||||
if pk = self.class.primary_key
|
if pk = self.class.primary_key
|
||||||
sync_with_transaction_state
|
sync_with_transaction_state
|
||||||
read_attribute(pk)
|
_read_attribute(pk)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def method_body(method_name, const_name)
|
|||||||
<<-EOMETHOD
|
<<-EOMETHOD
|
||||||
def #{method_name}
|
def #{method_name}
|
||||||
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
|
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
|
||||||
read_attribute(name) { |n| missing_attribute(n, caller) }
|
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
||||||
end
|
end
|
||||||
EOMETHOD
|
EOMETHOD
|
||||||
end
|
end
|
||||||
@ -64,7 +64,7 @@ def define_method_attribute(name)
|
|||||||
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
||||||
def #{temp_method}
|
def #{temp_method}
|
||||||
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
||||||
read_attribute(name) { |n| missing_attribute(n, caller) }
|
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
||||||
end
|
end
|
||||||
STR
|
STR
|
||||||
|
|
||||||
@ -84,13 +84,19 @@ def #{temp_method}
|
|||||||
def read_attribute(attr_name, &block)
|
def read_attribute(attr_name, &block)
|
||||||
name = attr_name.to_s
|
name = attr_name.to_s
|
||||||
name = self.class.primary_key if name == ID
|
name = self.class.primary_key if name == ID
|
||||||
@attributes.fetch_value(name, &block)
|
_read_attribute(name, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This method exists to avoid the expensive primary_key check internally, without
|
||||||
|
# breaking compatibility with the read_attribute API
|
||||||
|
def _read_attribute(attr_name) # :nodoc:
|
||||||
|
@attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def attribute(attribute_name)
|
def attribute(attribute_name)
|
||||||
read_attribute(attribute_name)
|
_read_attribute(attribute_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,8 +27,8 @@ def keys
|
|||||||
attributes.initialized_keys
|
attributes.initialized_keys
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_value(name, &block)
|
def fetch_value(name)
|
||||||
self[name].value(&block)
|
self[name].value { |n| yield n if block_given? }
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_from_database(name, value)
|
def write_from_database(name, value)
|
||||||
|
@ -142,7 +142,7 @@ def _enum_methods_module
|
|||||||
private
|
private
|
||||||
def save_changed_attribute(attr_name, old)
|
def save_changed_attribute(attr_name, old)
|
||||||
if (mapping = self.class.defined_enums[attr_name.to_s])
|
if (mapping = self.class.defined_enums[attr_name.to_s])
|
||||||
value = read_attribute(attr_name)
|
value = _read_attribute(attr_name)
|
||||||
if attribute_changed?(attr_name)
|
if attribute_changed?(attr_name)
|
||||||
if mapping[old] == value
|
if mapping[old] == value
|
||||||
clear_attribute_changes([attr_name])
|
clear_attribute_changes([attr_name])
|
||||||
|
@ -287,7 +287,7 @@ def initialize(name, scope, options, active_record)
|
|||||||
def association_scope_cache(conn, owner)
|
def association_scope_cache(conn, owner)
|
||||||
key = conn.prepared_statements
|
key = conn.prepared_statements
|
||||||
if polymorphic?
|
if polymorphic?
|
||||||
key = [key, owner.read_attribute(@foreign_type)]
|
key = [key, owner._read_attribute(@foreign_type)]
|
||||||
end
|
end
|
||||||
@association_scope_cache[key] ||= @scope_lock.synchronize {
|
@association_scope_cache[key] ||= @scope_lock.synchronize {
|
||||||
@association_scope_cache[key] ||= yield
|
@association_scope_cache[key] ||= yield
|
||||||
|
@ -79,7 +79,7 @@ def scope_relation(record, table, relation)
|
|||||||
scope_value = record.send(reflection.foreign_key)
|
scope_value = record.send(reflection.foreign_key)
|
||||||
scope_item = reflection.foreign_key
|
scope_item = reflection.foreign_key
|
||||||
else
|
else
|
||||||
scope_value = record.read_attribute(scope_item)
|
scope_value = record._read_attribute(scope_item)
|
||||||
end
|
end
|
||||||
relation = relation.and(table[scope_item].eq(scope_value))
|
relation = relation.and(table[scope_item].eq(scope_value))
|
||||||
end
|
end
|
||||||
|
@ -22,7 +22,7 @@ def test_class_with_blank_sti_name
|
|||||||
company = Company.first
|
company = Company.first
|
||||||
company = company.dup
|
company = company.dup
|
||||||
company.extend(Module.new {
|
company.extend(Module.new {
|
||||||
def read_attribute(name)
|
def _read_attribute(name)
|
||||||
return ' ' if name == 'type'
|
return ' ' if name == 'type'
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user