| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
| |
|
|
|
|
|
|
|
| |
This API will require much less consuming code to change to accomodate
the removal of automatic type casting from Arel. As long as the
predicates are constructed using the `arel_table` off of an AR subclass,
there will be no changes that need to happen.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This class cares far too much about the internals of other parts of
Active Record. This is an attempt to break out a meaningful object which
represents the needs of the predicate builder. I'm not fully satisfied
with the name, but the general concept is an object which represents a
table, the associations to/from that table, and the types associated
with it. Many of these exist at the `ActiveRecord::Base` class level,
not as properties of the table itself, hence the need for another
object. Currently it provides these by holding a reference to the class,
but that will likely change in the future. This allows the predicate
builder to remain wholy concerned with building predicates.
/cc @mrgilman
|
|
|
|
|
|
|
| |
Construction of relations can be a hotspot, we don't want to create one
of these in the constructor. This also allows us to do more expensive
things in the predicate builder's constructor, since it's created once
per AR::Base subclass
|
|
|
|
|
|
|
|
| |
If there is a method defined such as `find_and_do_stuff(id)`, which then
gets called on an association, we will perform statement caching and the
parent ID will not change on subsequent calls.
Fixes #18117
|
|
|
|
|
|
|
|
|
|
|
|
| |
This `# :nodoc:` had the effect of hiding every method that follows.
This meant that the API page for `ActiveRecord::Core` only contained
`configurations` and none of the following methods.
Furthermore this `# :nodoc:` had no effect on `maintain_test_schema`.
Those `mattr_accessor` inside the `included` block are not picked up
by rdoc.
/cc @zzak
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We never actually make use of it on the table, since we're constructing
the select manager manually. It looks like if we ever actually were
grabbing it from the table, we're grossly misusing it since it's meant
to vary by AR class.
Its existence on `Arel::Table` appears to be purely for convenience
methods that are never used outside of tests. However, in production
code it just complicates construction of the tables on the rails side,
and the plan is to remove it from `Arel::Table` entirely. I'm not
convinced it needs to live on `SelectManager`, etc either.
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
| |
Sufficiently large integers cause `find` and `find_by` to raise
`StatementInvalid` instead of `RecordNotFound` or just returning `nil`.
Given that we can't cast to `nil` for `Integer` like we would with junk
data for other types, we raise a `RangeError` instead, and rescue in
places where it would be highly unexpected to get an exception from
casting.
Fixes #17380
|
|
|
|
|
| |
It is internal use only. This is to avoid conflicting with users' method
names. Fixes #17458
|
|
|
|
|
|
|
|
|
|
|
|
| |
The current style for warning messages without newlines uses
concatenation of string literals with manual trailing spaces
where needed.
Heredocs have better readability, and with `squish` we can still
produce a single line.
This is a similar use case to the one that motivated defining
`strip_heredoc`, heredocs are super clean.
|
|
|
|
| |
This will avoid naming clash with user defined methods
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Inspired by @tenderlove's work in
c363fff29f060e6a2effe1e4bb2c4dd4cd805d6e, this reduces the number of
strings allocated when running callbacks for ActiveRecord instances. I
measured that using this script:
```
require 'objspace'
require 'active_record'
require 'allocation_tracer'
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
result = ObjectSpace::AllocationTracer.trace do
N.times { Article.find a.id }
end
result.sort.each do |k,v|
p k => v
end
puts "total: #{result.values.map(&:first).inject(:+)}"
```
When I run this against master and this branch I get this output:
```
pete@balloon:~/projects/rails/activerecord$ git checkout master
M Gemfile
Switched to branch 'master'
pete@balloon:~/projects/rails/activerecord$ bundle exec ruby benchmark_allocation_with_callback_send.rb > allocations_before
pete@balloon:~/projects/rails/activerecord$ git checkout remove-dynamic-send-on-built-in-callbacks
M Gemfile
Switched to branch 'remove-dynamic-send-on-built-in-callbacks'
pete@balloon:~/projects/rails/activerecord$ bundle exec ruby benchmark_allocation_with_callback_send.rb > allocations_after
pete@balloon:~/projects/rails/activerecord$ diff allocations_before allocations_after
39d38
<
{["/home/pete/projects/rails/activesupport/lib/active_support/callbacks.rb",
81]=>[40, 0, 0, 0, 0, 0]}
42c41
< total: 630
---
> total: 590
```
In addition to this, there are two micro-optimizations present:
* Using `block.call if block` vs `yield if block_given?` when the block was being captured already.
```
pete@balloon:~/projects$ cat benchmark_block_call_vs_yield.rb
require 'benchmark/ips'
def block_capture_with_yield &block
yield if block_given?
end
def block_capture_with_call &block
block.call if block
end
def no_block_capture
yield if block_given?
end
Benchmark.ips do |b|
b.report("block_capture_with_yield") { block_capture_with_yield }
b.report("block_capture_with_call") { block_capture_with_call }
b.report("no_block_capture") { no_block_capture }
end
pete@balloon:~/projects$ ruby benchmark_block_call_vs_yield.rb
Calculating -------------------------------------
block_capture_with_yield
124979 i/100ms
block_capture_with_call
138340 i/100ms
no_block_capture 136827 i/100ms
-------------------------------------------------
block_capture_with_yield
5703108.9 (±2.4%) i/s - 28495212 in 4.999368s
block_capture_with_call
6840730.5 (±3.6%) i/s - 34169980 in 5.002649s
no_block_capture 5821141.4 (±2.8%) i/s - 29144151 in 5.010580s
```
* Defining and calling methods instead of using send.
```
pete@balloon:~/projects$ cat benchmark_method_call_vs_send.rb
require 'benchmark/ips'
class Foo
def tacos
nil
end
end
my_foo = Foo.new
Benchmark.ips do |b|
b.report('send') { my_foo.send('tacos') }
b.report('call') { my_foo.tacos }
end
pete@balloon:~/projects$ ruby benchmark_method_call_vs_send.rb
Calculating -------------------------------------
send 97736 i/100ms
call 151142 i/100ms
-------------------------------------------------
send 2683730.3 (±2.8%) i/s - 13487568 in 5.029763s
call 8005963.9 (±2.7%) i/s - 40052630 in 5.006604s
```
The result of this is making typical ActiveRecord operations slightly faster:
https://gist.github.com/phiggins/e46e51dcc7edb45b5f98
|
|
|
|
|
|
|
|
|
| |
For now, we will just skip the cache when a non-column key is used in the hash.
If the future, we can probably move some of the logic in PredicateBuilder.expand
up the chain to make caching possible for association queries.
Closes #16903
Fixes #16884
|
|
|
|
|
|
| |
This is pretty lame though, so feel free to send a PR with a better fix! ;)
cc @zev @tenderlove
|
|
|
|
|
|
|
| |
Maintain compatibility for:
* ActiveRecord::Base#find(:all)
* ActiveRecord::Base#find(:first)
* ActiveRecord::Base#find(:last)
|
|
|
|
|
|
|
|
|
|
| |
Closes #16684.
This is achieved by always generating `GeneratedAssociationMethods` when
`ActiveRecord::Base` is subclassed. When some of the included modules
of `ActiveRecord::Base` were reordered this behavior was broken as
`Core#initialize_generated_modules` was no longer called. Meaning that
the module was generated on first access.
|
|
|
|
|
|
| |
Eagerly loaded collection and singular associations are ignored by the StatementCache, which causes errors when the queries they generate reference columns that were not eagerly loaded.
This commit skips the creation of the StatementCache as a fix for these scenarios.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
* Also duplicated find_by tests from relations_test.rb to finder_test.rb now
that we have a completely different implementation on the class (in core.rb
with AST caching stuff).
* Also removed a (failing) test that used mocks. Now that we have tests for the
behavior, there's no point having another test that tests the implementation
(that it delegates). Further, what the test was implying is nolonger true with
the current implementation, because Class.find_by is a real method now.
|
|\
| |
| | |
Fixed a regression introduced in 84cf156
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
84cf156 (PR #15694) introduced a subtle regression. There are actually three
distinct entry points to creating an AR object – via .new (i.e. #initialize),
via #init_with (e.g. from YAML or database queries) and via .allocate.
With the patch in 84cf156, attribute methods and respond_to? will not work
correctly when objects are allocated directly, without going through either
The reason this test case didn't catch the regression was that the `Topic`
class is shared between test cases, so by the time this test case is ran the
attribute methods are very likely to be defined.
Switching to use a fresh anonymous class in the test to ensure we surface this
problem in the future.
|
|\ \
| | |
| | |
| | |
| | |
| | |
| | | |
Move pk initialization logic onto `AttributeSet`
Conflicts:
activerecord/lib/active_record/attribute_set.rb
|
| | |
| | |
| | |
| | | |
Better encapsulates its internals from `ActiveRecord::Base`.
|
|/ / |
|
| |
| |
| |
| | |
`AttributeSet#dup` has all the behavior we need.
|
| |
| |
| |
| |
| |
| | |
Making this change revealed several subtle bugs related to models with
no primary key, and anonymous classes. These have been fixed as well,
with regression tests added.
|
| |
| |
| |
| |
| |
| |
| |
| | |
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
|
| |
| |
| |
| | |
This was used more previously, but other uses have been removed.
|
| | |
|
| |
| |
| |
| |
| | |
The :singleton-method: directive is printed in the docs.
This directive is not necessary as the method is documentable just fine.
|
|/
|
|
|
| |
Mostly delegation to start, but we can start moving a lot of behavior in
bulk to this object.
|
| |
|
|\
| |
| | |
Ensure we always define attribute methods
|
| | |
|
|\ \
| |/
|/| |
Introduce an Attribute object to handle the type casting dance
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
There's a lot more that can be moved to these, but this felt like a good
place to introduce the object. Plans are:
- Remove all knowledge of type casting from the columns, beyond a
reference to the cast_type
- Move type_cast_for_database to these objects
- Potentially make them mutable, introduce a state machine, and have
dirty checking handled here as well
- Move `attribute`, `decorate_attribute`, and anything else that
modifies types to mess with this object, not the columns hash
- Introduce a collection object to manage these, reduce allocations, and
not require serializing the types
|
| | |
|
|/
|
|
|
|
|
|
|
|
|
| |
We'd spend a lot of time calling `hash` and `eql?` on the join model,
which has no primary key. Calling `id` with no primary key is a really
slow way to get back `nil`, so we can improve the performance there.
However, even with the escape clause, we *still* weren't getting high
enough performance, as we were checking the primary key too much. `hash`
will always return `nil.hash` for records with no id, and `==` will
always return `false`. We can optimize those cases in the HABTM join
model.
|
|
|
|
|
| |
On MySQL and PostgreSQL, the adapter does not type cast virtual columns
for us.
|
|
|
|
|
|
|
| |
Reduces the number of places that care about the internals of how we
store and type cast attributes. We do not need to go through the
dup/freeze dance, as you couldn't have saved a frozen new record anyway,
and that is the only time we would end up modifying the frozen hash.
|
|
|
|
|
| |
`@raw_attributes` should not contain the type-cast, mutable version of
the value.
|
|\
| |
| |
| |
| |
| |
| | |
implement ActiveRecord::Base#pretty_print
Conflicts:
activerecord/CHANGELOG.md
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The contract of `_field_changed?` assumes that the old value is always
type cast. That is not the case for the value in `Column#default` as
things are today. It appears there are other public methods that
assume that `Column#default` is type cast, as well. The reason for this
change originally was because the value gets put into `@raw_attributes`
in initialize. This reverts to the old behavior on `Column`, and updates
`initialize` to make sure that the values are in the right format.
|
| |
| |
| |
| |
| | |
This was previously a hook for a special case related to `serialize`,
which has since been removed.
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Nearly completely implemented in terms of custom properties.
`_before_type_cast` now stores the raw serialized string consistently,
which removes the need to keep track of "state". The following is now
consistently true:
- `model.serialized == model.reload.serialized`
- A model can be dumped and loaded infinitely without changing
- A model can be saved and reloaded infinitely without changing
|
|/
|
|
|
|
|
|
|
| |
`@attributes` was actually used for `_before_type_cast` and friends,
while `@attributes_cache` is the type cast version (and caching is the
wrong word there, but I'm working on removing the conditionals around
that). I opted for `@raw_attributes`, because `_before_type_cast` is
also semantically misleading. The values in said hash are in the state
given by the form builder or database, so raw seemed to be a good word.
|