Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable scoping to apply to all queries #41397

Merged

Conversation

eileencodes
Copy link
Member

@eileencodes eileencodes commented Feb 10, 2021

Similar to #40720
and #40805 this change allows for the
scoping method to apply to all queries in the block. Previously this
would only apply to queries on the class and not the instance. Ie
Post.create, Post.all, but not post.update, or post.delete.

The change here will create a global scope that is applied to all
queries for a relation for the duration of the block.

Benefits:

This change allows applications to add a scope to any query for the
duration of a block. This is useful for applications using sharding to
be able to control the query without requiring a default_scope. This
is useful if you want to have more control over when a scoping is used
on a relation. This also brings scoping in parity with the behavior of
default_scope so there are less surprises between the behavior of
these two methods.

There are a caveats to this behavior:

  1. The scoping only applies to objects of the same type. IE you cannot
    scope Post.where(blog_id: 1).scoping and then expect post.comments
    will apply blog_id = 1 to the Comment query. This is not possible
    because the scope is posts.blog_id = 1 and we can't apply the posts
    scope to a comments query. To solve this, scopes must be nested.
  2. If a block is scoped to all_queries it cannot be unscoped without
    exiting the block. I couldn't find a way around this but ActiveRecord
    scoping is a bit complex and turning off all_queries when it's already
    on in nested scoping blocks had interesting behavior that I decided was
    best left out.

@eileencodes eileencodes force-pushed the allow-scoping-to-apply-to-all-queries branch from 5a0ce09 to 143af37 Compare February 10, 2021 19:48
@eileencodes eileencodes force-pushed the allow-scoping-to-apply-to-all-queries branch 2 times, most recently from c201676 to 0bbec24 Compare February 10, 2021 19:56
```ruby
Post.where(blog_id: post.blog_id).scoping(all_queries: true) do
post.update(title: "a post title") # adds `posts.blog_id = 1` to the query
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
end
end
```

@@ -99,6 +99,8 @@ def target=(target)
def scope
if (scope = klass.current_scope) && scope.try(:proxy_association) == self
scope.spawn
elsif (scope = klass.global_current_scope)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elsif (scope = klass.global_current_scope)
elsif scope = klass.global_current_scope

@eileencodes eileencodes force-pushed the allow-scoping-to-apply-to-all-queries branch 2 times, most recently from 5e66367 to e4dd1ca Compare February 11, 2021 21:03
Similar to rails#40720
and rails#40805 this change allows for the
`scoping` method to apply to all queries in the block. Previously this
would only apply to queries on the class and not the instance. Ie
`Post.create`, Post.all`, but not `post.update`, or `post.delete`.

The change here will create a global scope that is applied to all
queries for a relation for the duration of the block.

Benefits:

This change allows applications to add a scope to any query for the
duration of a block. This is useful for applications using sharding to
be able to control the query without requiring a `default_scope`. This
is useful if you want to have more control over when a `scoping` is used
on a relation. This also brings `scoping` in parity with the behavior of
`default_scope` so there are less surprises between the behavior of
these two methods.

There are a caveats to this behavior:

1) The `scoping` only applies to objects of the same type. IE you cannot
scope `Post.where(blog_id: 1).scoping` and then expect `post.comments`
will apply `blog_id = 1` to the `Comment` query. This is not possible
because the scope is `posts.blog_id = 1` and we can't apply the `posts`
scope to a `comments` query. To solve this, scopes must be nested.
2) If a block is scoped to `all_queries` it cannot be unscoped without
exiting the block. I couldn't find a way around this but ActiveRecord
scoping is a bit complex and turning off `all_queries` when it's already
on in nested scoping blocks had interesting behavior that I decided was
best left out.
@eileencodes eileencodes force-pushed the allow-scoping-to-apply-to-all-queries branch from e4dd1ca to d6e12ff Compare February 11, 2021 21:04
@eileencodes eileencodes merged commit 9eedc37 into rails:main Feb 11, 2021
@eileencodes eileencodes deleted the allow-scoping-to-apply-to-all-queries branch February 11, 2021 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants