This is an alternative to https://github.com/rails/rails/pull/34195
The active record `respond_to?` method needs to do two things if `super` does not say that the method exists. It has to see if the "name" being passed in represents a column in the table. If it does then it needs to pass it to `has_attribute?` to see if the key exists in the current object. The reason why this is slow is that `has_attribute?` needs a string and most (almost all) objects passed in are symbols.
The only time we need to allocate a string in this method is if the column does exist in the database, and since these are a limited number of strings (since column names are a finite set) then we can pre-generate all of them and use the same string.
We generate a list hash of column names and convert them to symbols, and store the value as the string name. This allows us to both check if the "name" exists as a column, but also provides us with a string object we can use for the `has_attribute?` call.
I then ran the test suite and found there was only one case where we're intentionally passing in a string and changed it to a symbol. (However there are tests where we are using a string key, but they don't ship with rails).
As re-written this method should never allocate unless the user passes in a string key, which is fairly uncommon with `respond_to?`.
This also eliminates the need to special case every common item that might come through the method via the `case` that was originally added in f80aa59946 (by me) and then with an attempt to extend in https://github.com/rails/rails/pull/34195.
As a bonus this reduces 6,300 comparisons (in the CodeTriage app homepage) to 450 as we also no longer need to loop through the column array to check for an `include?`.
* Fix `ActionController::Parameters#each_value`
`each_value` should yield with "value" of the params instead of "value" as an array.
Related to #33979
* Add changelog entry about `ActionController::Parameters#each_value`.
Follow up #33979
`developers.name desc` was added at d59f3a7, but any test case isn't
failed even if the `developers.name desc` is removed since all tested
developers are consistently ordered on both `name` and `id`.
I changed one developers creation ordering to ensure to test that
`project.developers` is ordered by `developers.name desc`.
Resolve Issue#29200
When scaffolding a model that references another model the
generated show and index html pages display the object directly
on the page. Basically, it just shows a memory address. That is
not very helpful. In this commit we show the object's id rather
than the memory address.
This updates the scaffold templates and the json builder files.
Up to `2.7.0`, encoding was chosen using `Mail::Encodings::TransferEncoding.negotiate`,
and base64 encoding was used.
In `2.7.1`, when `transfer_encoding` is not specified, the encoding
of the message is respected.
Related to: dead487e02
However, what chosen for transfer encoding is not essential in these tests.
To test more accurately, confirm that the decoded body instead.
- Adding a Float as a duration to a datetime would result in the Float
being rounded. Doing something like would have no effect because the
0.45 seconds would be rounded to 0 second.
```ruby
time = DateTime.parse("2018-1-1")
time += 0.45.seconds
```
This behavior was intentionally added a very long time ago, the
reason was because Ruby 1.8 was using `Integer#gcd` in the
constructor of Rational which didn't accept a float value.
That's no longer the case and doing `Rational(0.45, 86400)` would
now perfectly work fine.
- Fixes#34008
Ruby uses the original method name, so will show the __temp__ method
name in the backtrace. However, in the common case the method name
is compatible with the `def` keyword, so we can avoid the __temp__
method name in that case to improve the name shown in backtraces
or TracePoint#method_id.
`assert_enqueued_with` and `assert_performed_with` return a instantiated
instance of the matching job for further assertion (#21010).
Before this commit the `arguments` method on the returned instance
returns a serialized version of the arguments.
This PR adds the ability to 1) connect to multiple databases in a model,
and 2) switch between those connections using a block.
To connect a model to a set of databases for writing and reading use
the following API. This API supercedes `establish_connection`. The
`writing` and `reading` keys represent handler / role names and
`animals` and `animals_replica` represents the database key to look up
the configuration hash from.
```
class AnimalsBase < ApplicationRecord
connects_to database: { writing: :animals, reading: :animals_replica }
end
```
Inside the application - outside the model declaration - we can switch
connections with a block call to `connected_to`.
If we want to connect to a db that isn't default (ie readonly_slow) we
can connect like this:
Outside the model we may want to connect to a new database (one that is
not in the default writing/reading set) - for example a slow replica for
making slow queries. To do this we have the `connected_to` method that
takes a `database` hash that matches the signature of `connects_to`. The
`connected_to` method also takes a block.
```
AcitveRecord::Base.connected_to(database: { slow_readonly: :primary_replica_slow }) do
ModelInPrimary.do_something_thats_slow
end
```
For models that are already loaded and connections that are already
connected, `connected_to` doesn't need to pass in a `database` because
you may want to run queries against multiple databases using a specific
role/handler.
In this case `connected_to` can take a `role` and use that to swap on
the connection passed. This simplies queries - and matches how we do it
in GitHub. Once you're connected to the database you don't need to
re-connect, we assume the connection is in the pool and simply pass the
handler we'd like to swap on.
```
ActiveRecord::Base.connected_to(role: :reading) do
Dog.read_something_from_dog
ModelInPrimary.do_something_from_model_in_primary
end
```
When defining a Hash enum it can be easy to use [] instead of {}. This
commit checks that only valid definition values are provided, those can
be a Hash, an array of Symbols or an array of Strings. Otherwise it
raises an ArgumentError.
Fixes#33961
Before:
```
Pet Destroy (0.8ms) DELETE FROM `pets` WHERE `pets`.`pet_id` IN (SELECT `pet_id` FROM (SELECT DISTINCT `pets`.`pet_id` FROM `pets` LEFT OUTER JOIN `toys` ON `toys`.`pet_id` = `pets`.`pet_id` WHERE `toys`.`name` = ?) AS __active_record_temp) [["name", "Bone"]]
```
After:
```
Pet Destroy (1.0ms) DELETE `pets` FROM `pets` LEFT OUTER JOIN `toys` ON `toys`.`pet_id` = `pets`.`pet_id` WHERE `toys`.`name` = ? [["name", "Bone"]]
```