| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
| |
Fixes #18871
|
|
|
|
|
|
|
|
|
| |
This method doesn't need to be lazy, as it is never called from reads.
The only time it is called are in write cases, where we're about to loop
through the results of it, and build the attribute objects anyway. So we
don't gain anything by dodging the instantiation here. This is the only
method that coupled `AttributeSet` to `LazyAttributeHash`, so removing
it puts us back in a place where we can use a normal hash instead.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`freeze` will ultimately end up freezing the `AttributeSet`, which in
turn freezes its `@attributes` hash. However, we actually insert a
special object to lazily instantiate the values of the hash on demand.
When it does need to actually instantiate all of them for iteration (the
only case is `ActiveRecord::Base#attributes`, which calls
`AttributeSet#to_h`), it will set an instance variable as a performance
optimization
Since it's just an optimization for subsequent calls, and that method
being called at all is a very uncommon case, we can just leave the ivar
alone if we're frozen, as opposed to coming up with some overly
complicated mechanism for freezing which allows us to continue to modify
ourselves.
Fixes #17960
|
|
|
|
| |
This appears to be a performance hotspot, see #17655.
|
| |
|
|
|
|
| |
Performance improvement, as well as improved code clarity
|
|
|
|
| |
This class no longer has any private methods
|
| |
|
|
|
|
|
|
|
|
| |
`default_proc` makes a hash unmarshallable, and adds unneccessary
overhead. Since we control all access to the hash, let's just handle it
in that method. This has the side effect of improving performance on
initialization (but not neccessarily on access). We'll need to profile
further once the tests are passing.
|
|
|
|
|
|
|
| |
In real usage, we give the builder a types hash with a default value of
`Type::Value.new`. This means we need to explicitly check for the key,
rather than the truthiness of the type to determine if it's a known but
uninitialized attribute
|
|
|
|
|
|
|
|
|
|
| |
We don't know which attributes will or won't be used, and we don't want
to create massive bottlenecks at instantiation. Rather than doing *any*
iteration over types and values, we can lazily instantiate the object.
The lazy attribute hash should not fully implement hash, or subclass
hash at any point in the future. It is not meant to be a replacement,
but instead implement its own interface which happens to overlap.
|
|
|
|
|
|
|
|
|
|
| |
This reverts commit 8fee923888192a658d8823b31e77ed0683dfd665.
Conflicts:
activerecord/lib/active_record/attribute_set/builder.rb
This solution sucks, and is hard to actually apply across the board.
Going to try other solutions
|
| |
|
|
|
|
|
|
|
| |
We introduced a performance hit by adding an additional iteration
through a model's attributes on creation. We don't actually need the
values from `Result` to be a hash, we can separate the columns and
values and zip them up ourself during the iteration that we have to do.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Benchmark:
```ruby
require 'objspace'
require 'active_record'
ActiveRecord::Base.establish_connection adapter: "sqlite3",
database: ":memory:"
ActiveRecord::Base.connection.instance_eval do
create_table(:articles) { |t| t.string :name }
end
class Article < ActiveRecord::Base; end
a = Article.create name: "foo"
a = Article.find a.id
N = 10
ObjectSpace::AllocationTracer.trace do
N.times { Article.find a.id }
end
ObjectSpace::AllocationTracer.allocated_count_table
table.sort_by { |_,x| x }.each do |k,v|
p k => (v / N)
end
```
|
|
|
|
|
| |
This allows using polymorphism for the uninitialized attributes raising
an exception behavior.
|
|
|
|
|
|
|
|
| |
This will make it less painful to add additional properties, which
should persist across writes, such as `name`.
Conflicts:
activerecord/lib/active_record/attribute_set.rb
|
|
Moved `Builder` to its own file, as it started looking very weird once I
added private methods to the `AttributeSet` class and the `Builder`
class started to grow.
Would like to refactor `fetch_value` to change to
```ruby
self[name].value(&block)
```
But that requires the attributes to know about their name, which they
currently do not.
|