Commit Graph

82 Commits

Author SHA1 Message Date
Andrew Novoselac
e50182a42c Make the output of ActiveRecord::Core#inspect configurable.
By default, calling `inspect` on a record will yield a formatted string including just the `id`.

```ruby
Post.first.inspect #=> "#<Post id: 1>"
```

The attributes to be included in the output of `inspect` can be configured with
`ActiveRecord::Core#attributes_for_inspect`.

```ruby
Post.attributes_for_inspect = [:id, :title]
Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
```

With the `attributes_for_inspect` set to `:all`, `inspect` will list all the record's attributes.

```ruby
Post.attributes_for_inspect = :all
Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
```
2023-11-08 14:23:16 -05:00
Joé Dupuis
4b3571803b Fix regression blocking creation of some strict association
PR #48606 introduced a regression preventing the creation of a chain
of models using a has_one through association when strict_loading is
activated.

The association relies on the initial stale_state being `nil`
to skip loading the target.
755c13f167/activerecord/lib/active_record/associations/association.rb (L175)

PR #48606 initializes the stale_state to `[nil]`
https://github.com/rails/rails/pull/48606/files#diff-39c3da3c5f3fbff01b0a32d3b7ec613fda6bc6225fdbe2629134d91babe37786R84-R86

This change converts the `[nil]` state back to `nil`.
2023-08-21 10:32:08 -07:00
Jean Boussier
e4009c8d66 ActiveRecord::Delegation prevent overriding existing methods
I discovered this while working on https://github.com/rails/rails/pull/47800

The bug is quite subtle.

If you call `Model.all.delete(id)`, `all` is an `ActiveRecord::Relation`
which does not respond to `delete`, so it delegates to `Model.delete`
and generate that method in the `GeneratedRelationMethods` module.

The problem however, is that `CollectionProxy` does define `delete`,
so after that initial call, the `Model::ActiveRecord_CollectionProxy`
subclass now has its `delete` method overridden, and now delegate
to the model.

Here I chose to keep that method generation caching, but I'm honestly
not convinced it's really needed. I question how much of a hotspot
these methods are, and we're busting method caches and generating
a lot of code to save on a minor `method_missing` call.

I think we should just remove that caching.
2023-03-30 11:52:53 +02:00
Nikita Vasilevsky
07bae8b2f0 Change DeveloperWithDefaultNilableMentorScopeAllQueries name 2022-05-24 21:48:28 +00:00
Nathaniel Watts
da05fa3381
Fix for multiple default_scope all_queries options
In our use case - we have a base model that has a default scope that we
want enabled for all queries, ex:

```ruby
class Developer < ApplicationRecord
  default_scope -> { where(firm_id: Current.firm_id) }, all_queries:
  true
end
```

We're also leveraging a module that will add a default scope to only
find soft-deleted records.

```ruby
module SoftDeletable
  extend ActiveSupport::Concern

  included do
    default_scope { where(deleted_at: nil) }
  end
```

Through this, we've found that when using default scopes in combination,
*specifically in the use case where the _non_ all queries scope is
declared first*, that we would get an error when calling `.update`:

```ruby
class Developer < ApplicationRecord
  include SoftDeletable

  default_scope -> { where(firm_id: Current.firm_id) }, all_queries:
  true
```

```ruby
Current.firm_id = 5

developer = Developer.new(name: "Steve")

developer.update(name: "Stephen")

NoMethodError: undefined method `where' for nil:NilClass
```

In digging into the code, this was due to there not being an `else` case
for the `inject` used to combine `default_scopes` together (`inject`
  uses the return value of the block as the memoizer).

Without the `else` case, if the block returned `nil`, `nil` was passed
to the evaluation of the next `default_scope`.

This commit adds the `else`, and also makes a minor adjustment in
variable naming (`default_scope` to `combined_scope`) in an effort to
add a little more readability, as we're iterating over an array of
default scopes, but what we're building is _the_ default scope to be
used in the query, etc.

Co-authored-by: Will Cosgrove <will@cosgrove.email>
2022-05-09 16:34:18 -04:00
Nikita Vasilevsky
43d83b4423
Fix update & destroy queries when default_scope is nillable with all_queries: true 2021-09-16 14:12:11 -04:00
eileencodes
226007daa1
Handle false in relation strict loading checks
Fixes: #41453
Closes: #41461

Previously when a model had strict loading set to true and then had a
relation set strict_loading to false the false wasn't considered when
deciding whether to raise/warn about strict loading.

Code example:

```ruby
class Dog < ActiveRecord::Base
  self.strict_loading_by_default = true

  has_many :treats, strict_loading: false
end
```

In the example, `dog.treats` would still raise even though
`strict_loading` was set to false. This is a bug effecting more than
Active Storage which is why I made this PR superceeding #41461. We need
to fix this for all applications since the behavior is a little
surprising. I took the test from ##41461 and the code suggestion from #41453
with some additions.

Co-authored-by: Radamés Roriz <radamesroriz@gmail.com>
2021-03-16 16:26:59 -04:00
Ryuta Kamizono
cbb19eb833 Fix complicated has_many through with nested where condition
#40779 will be caused if (1) having nested where condition for through
association, and (2) through scope has references values, and (3) the
model has no association which is the same name with table name.

In that case, the join root will be found by the table name but the join
root isn't related with association reflection.

This changes to return the table klass from join dependency tree, it
works regardless of whether the join root or join association children.

Fixes #40779.
2021-01-05 11:40:04 +09:00
eileencodes
bda9bc013b
Fix strict loading on validations
We don't want strict loading to throw an error on validations since
those need to load the record in order to validate it.

This change check the owners's `validation_context`. If it's `nil` then
we know we're not currently validating an object in create/update. If
it's set then we're validating and want to skip raising for strict
loading.

Fixes: #40767
2020-12-09 13:42:24 -05:00
eileencodes
816f6194b6
Add option for default_scope to run on all queries
This change allows for applications to optionally run a `default_scope`
on `update` and `delete` queries. Default scopes already ran on select
and insert queries.

Applications can now run a set default scope on all queries for a model
by setting a `all_queries` option:

```ruby
class Article < ApplicationRecord
  default_scope -> { where(blog_id: 1) }, all_queries: true
end
```

Using the default scope in this way is useful for applications that need
to query by more than the primary key by default. An example of this
would be in an application using a sharding strategy like Vitess like.
For Rails sharding, we route connections first and then query the
database. However, Vitess and other solutions use a parameter in the
query to figure out how to route the queries. By extending
`default_scope` to apply to all queries we can allow applications to
optionally apply additional constraints to all queries. Note that this
only works with `where` queries as it does not make sense to select a
record by primary key with an order. With this change we're allowing
apps to select with a primary key and an additional key.

To make this change dynamic for routing queries in a tenant sharding
strategy applications can use the `Current` API or parameters in a
request to route queries:

```ruby
class Article < ApplicationRecord
  default_scope -> { where(blog_id: Current.blog.id) }, all_queries: true
end
```

In order to achieve this I created a new object when default scopes are
created. This allows us to store both the scope itself and whether we
should run this on all queries. I chose not to implement an `on:` option
that takes an array of actions because there is no simple or clear way
to turn off the default scope for create/select. It also doesn't really
make sense to only have a default scope for delete queries. The decision
to use `all_queries` here allows for the implementation to be more
flexible than it was without creating a mess in an application.
2020-12-01 11:15:08 -05:00
Ryuta Kamizono
6d6ec6f936 Support where with comparison operators (>, >=, <, and <=)
```ruby
posts = Post.order(:id)

posts.where("id >": 9).pluck(:id)  # => [10, 11]
posts.where("id >=": 9).pluck(:id) # => [9, 10, 11]
posts.where("id <": 3).pluck(:id)  # => [1, 2]
posts.where("id <=": 3).pluck(:id) # => [1, 2, 3]
```

From type casting and table/column name resolution's point of view,
`where("create_at >=": time)` is better alternative than `where("create_at >= ?", time)`.

```ruby
class Post < ActiveRecord::Base
  attribute :created_at, :datetime, precision: 3
end

time = Time.now.utc # => 2020-06-24 10:11:12.123456 UTC

Post.create!(created_at: time) # => #<Post id: 1, created_at: "2020-06-24 10:11:12.123000">

# SELECT `posts`.* FROM `posts` WHERE (created_at >= '2020-06-24 10:11:12.123456')
Post.where("created_at >= ?", time) # => []

# SELECT `posts`.* FROM `posts` WHERE `posts`.`created_at` >= '2020-06-24 10:11:12.123000'
Post.where("created_at >=": time) # => [#<Post id: 1, created_at: "2020-06-24 10:11:12.123000">]
```
2020-07-06 10:24:48 +09:00
Ryuta Kamizono
abae7fcd5f Allow attribute aliases for timestamp magic columns
For now, timestamp magic columns are only allowed for real physical
columns, it is not a matter for newly created app, but it is harder to
get the usefulness for legacy databases.

The reason that doesn't work is some low-level API does not care about
attribute aliases. I think that uses low-level API without attribute
alias resolution for timestamp attributes is not intended (e.g.
`updated_at_before_type_cast` works, but `has_attribute?("updated_at")`
and `_read_attribute("updated_at")` doesn't work).

I've addressed all missing attribute alias resolution for timestamp
attributes to work that consistently.

Fixes #37554.
2020-06-01 21:29:33 +09:00
Flavio Wuensche
712ef53c9a Reload schema after ignored_columns reassignment
reload column_names after ignored_columns assignment

code review: remove unnecessary setup and move up reload_schema_from_cache
2020-04-23 07:50:06 +02:00
Kevin Deisz
b8ae104318
Support strict_loading on association declarations 2020-02-21 13:11:24 -05:00
eileencodes
dbb92f8a77
Add strict_loading mode to prevent lazy loading
Add `#strict_loading` to any record to prevent lazy loading of associations.
`strict_loading` will cascade down from the parent record to all the
associations to help you catch any places where you may want to use
`preload` instead of lazy loading. This is useful for preventing N+1's.

Co-authored-by: Aaron Patterson <aaron.patterson@gmail.com>
2020-02-20 08:32:48 -05:00
Ryuta Kamizono
21c89dcc75 Using define_attribute_method on ivar attrs is not officially supported
It doesn't work dirty tracking on Active Record. Use `attribute` API
instead.
2020-01-29 09:56:27 +09:00
Jean Boussier
cce26c36ca Namespace association extension modules under the owner model 2019-05-02 13:25:18 +02:00
Ryuta Kamizono
634953af94 Allow returning nil for default_scope 2019-02-28 00:24:23 +09:00
DmitryTsepelev
d34c1fc3d6 Cached columns_hash fields should be excluded from ResultSet#column_types 2018-11-27 16:30:26 +03:00
Altech
e9f82e76e8 Convert ignored_columns to a list of string 2017-10-20 19:42:44 +09:00
Ryuta Kamizono
207c37a1ec Test ignored_columns value is inheritable by subclasses 2017-10-19 21:07:56 +09:00
Kir Shatrov
831be98f9a Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
Matthew Draper
87b3e226d6 Revert "Merge pull request #29540 from kirs/rubocop-frozen-string"
This reverts commit 3420a14590c0e6915d8b6c242887f74adb4120f9, reversing
changes made to afb66a5a598ce4ac74ad84b125a5abf046dcf5aa.
2017-07-02 02:15:17 +09:30
Kir Shatrov
cfade1ec7e Enforce frozen string in Rubocop 2017-07-01 02:11:03 +03:00
Chris Holmes
f8ab3ae18f Raise error when has_many through is defined before through association
https://github.com/rails/rails/issues/26834

This change raises an error if a has_many through association
is defined before the through association.
2017-01-04 11:56:08 +00:00
Matthew Draper
dae525eec8 Replace sleep with synchronization 2017-01-02 05:04:17 +10:30
Xavier Noria
b326e82dc0 applies remaining conventions across the project 2016-08-06 20:20:22 +02:00
Xavier Noria
d22e522179 modernizes hash syntax in activerecord 2016-08-06 19:37:57 +02:00
Xavier Noria
9617db2078 applies new string literal convention in activerecord/test
The current code base is not uniform. After some discussion,
we have chosen to go with double quotes by default.
2016-08-06 18:26:53 +02:00
Sean Griffin
5c32f41d52 Fix merge conflicts for #19938
This is a separate commit, as it is not just a changelog conflict. Want
to point out the changes in the code
2015-10-20 16:57:47 -06:00
eileencodes
ee824c8858 Fix regression in inverse_of on through associations
`inverse_of` on through associations was accidently removed/caused to
stop working in commit f8d2899 which was part of a refactoring on
`ThroughReflection`.

To fix we moved `inverse_of` and `check_validity_of_inverse!` to the
`AbstractReflection` so it's available to the `ThroughReflection`
without having to dup any methods. We then need to delegate `inverse_name`
method in `ThroughReflection`. `inverse_name` can't be moved to
`AbstractReflection` without moving methods that set the instance
variable `@automatic_inverse_of`.

This adds a test that ensures that `inverse_of` on a `ThroughReflection`
returns the correct class name, and the correct record for the inverse
relationship.

Fixes #21692
2015-09-26 16:26:56 -04:00
Jean Boussier
4ccdd4122d Implement ActiveRecord::Base.ignored_columns 2015-09-24 11:04:44 -04:00
Agis-
19b168e611 Only nullify persisted has_one target associations
Since after 87d1aba3c `dependent: :destroy` callbacks on has_one
assocations run *after* destroy, it is possible that a nullification is
attempted on an already destroyed target:

    class Car < ActiveRecord::Base
      has_one :engine, dependent: :nullify
    end

    class Engine < ActiveRecord::Base
      belongs_to :car, dependent: :destroy
    end

    > car = Car.create!
    > engine = Engine.create!(car: car)
    > engine.destroy! # => ActiveRecord::ActiveRecordError: cannot update a
    >   destroyed record

In the above case, `engine.destroy!` deletes `engine` and *then* triggers the
deletion of `car`, which in turn triggers a nullification of `engine.car_id`.
However, `engine` is already destroyed at that point.

Fixes #21223.
2015-08-24 11:49:43 +03:00
Mehmet Emin İNAÇ
df94dabb37 Fix for activerecord join dependency instantiate bug
use only object_id instead parent class and parent id

test cases

assert_equal

use table name in references

fix minor problems
2015-05-04 18:06:06 +03:00
Sammy Larbi
f43f56e16e Ensure HABTM relationships produce valid class names (Fixes #17119) 2014-11-09 11:56:07 -06:00
Agis-
431f8e0119 Only merge scopes with zero arity in has_many through
with dynamic conditions.

Fixes #16128

This bug was introduced in c35e438620
so it's present from 4.1.2-rc1 and after.

c35e438620
merges any relation scopes passed as proc objects to the relation,
but does *not* take into account the arity of the lambda.

To reproduce: https://gist.github.com/Agis-/5f1f0d664d2cd08dfb9b
2014-08-20 08:25:58 +03:00
Yves Senn
a0eec57ef0 test, inline DeveloperWithAggregate, which is used by a single test. 2014-05-19 14:11:45 +02:00
Fred Wu
f045663dfc Fixed HABTM's CollectionAssociation size
HABTM should fall back to using the normal CollectionAssociation's size calculation if the collection is not cached or loaded.

This addresses both #14913 and #14914 for master.
2014-05-08 16:01:57 +10:00
Jon Leighton
40a9d89877 Add tests for default scope behaviour change
See #13875
2014-02-23 11:14:05 +00:00
Aaron Patterson
da3891c898 make sure cached table name is a string. fixes #12582 2013-12-12 10:47:07 -08:00
Aaron Patterson
3e0a60e4e2 adding a test to demonstrate how to use STI subclasses on the far right
side of a hm:t association along with preloading.
2013-09-27 16:56:49 -07:00
Aaron Patterson
468d5b1cbb this code is dead, removing 2013-08-01 11:43:51 -07:00
Derek Kraan
bc4edca7b1 Fix .update_all and .delete_all when using a condition on a joined table
in a default_scope.

`Model.joins(...).where(condition_on_joined_table).update_all` /
`delete_all` worked, but the same operation implemented with a
default_scope generated a SQL error because ActiveRecord ignored the
join but implemented the where condition anyways.
2013-01-11 12:31:09 -05:00
Rafael Mendonça França
9cce1ea2fd Allow users to choose the timestamp format in the cache key
This can be done using the class attribute cache_timestamp_format

Conflicts:
	railties/guides/source/configuring.textile
2012-12-10 17:48:26 -03:00
Yves Senn
db51704bd9 Do not instantiate intermediate AR objects when eager loading.
Closes #3313
2012-12-04 11:52:08 +01:00
Rafael Mendonça França
f4d818d51e Revert "Removing composed_of from ActiveRecord."
This reverts commit 14fc8b34521f8354a17e50cd11fa3f809e423592.

Reason: we need to discuss a better path from this removal.

Conflicts:
	activerecord/lib/active_record/reflection.rb
	activerecord/test/cases/base_test.rb
	activerecord/test/models/developer.rb
2012-07-27 19:25:14 -03:00
Jon Leighton
b658cf1198 Deprecate ActiveRecord::Base.scoped.
It doesn't serve much purpose now that ActiveRecord::Base.all returns a
Relation.

The code is moved to active_record_deprecated_finders.
2012-07-27 17:27:47 +01:00
Jon Leighton
e1cfa6e070 Convert association macros to the new syntax 2012-07-20 14:14:51 +01:00
Carlos Antonio da Silva
4563995096 Remove some aggregation tests related to composed_of
Since composed_of was removed in 051747449e7afc817c599e4135bc629d4de064eb,
these tests were working "by mistake", due to the matching "address"
string in the error message, but with a different error message than the
expected multiparameter assignment error.

Since "address" is not an attribute from Customer anymore, the error was
"undefined method klass for nil", where nil was supposed to be the
column object.
2012-06-28 09:58:58 -03:00
Steve Klabnik
14fc8b3452 Removing composed_of from ActiveRecord.
This feature adds a lot of complication to ActiveRecord for dubious
value. Let's talk about what it does currently:

class Customer < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
end

Instead, you can do something like this:

    def balance
      @balance ||= Money.new(value, currency)
    end

    def balance=(balance)
      self[:value] = balance.value
      self[:currency] = balance.currency
      @balance = balance
    end

Since that's fairly easy code to write, and doesn't need anything
extra from the framework, if you use composed_of today, you'll
have to add accessors/mutators like that.

Closes #1436
Closes #2084
Closes #3807
2012-06-18 14:53:03 -04:00