How can I have something like an ActiveRecord scope
that modifies the relation it's acting on?
Specifically, I want to be able to do this:
Model.some_scope.fake_destroy_all
Where fake_destroy_all
is the functionality I'm trying to create. It'll be pretty much equivalent to .update_all(deleted: true)
.
A weak alternative would be:
def fake_destroy_all(relation = Model)
relation.update_all(deleted: true)
end
#...
Model.fake_destroy_all(Model.some_scope)
But that's not ideal. What I'd like to do is something like:
scope :fake_destroy_all, update_all(deleted: true)
But that doesn't work.
Is it possible to do something like what I'm describing?
Try this:
def self.fake_destroy_all
self.update_all(deleted: true)
end
It turns out that relations retain all the class methods. Scopes are class methods as well, and it's the exact mechanism that allows scopes to be "chained".
Therefore, relations with this model will also have this method with self
being an instance of a relation.
The scope syntax can probably be fixed like so (cannot test right now, but note the lambda instead of simple method call):
scope :fake_destroy_all, -> { update_all(deleted: true) }
I suppose, without updating inside a lambda it effectively calls this method on invoking scope
, on class definition. It does work with simple where
-type scopes because where
has no side-effect, it only returns an applicable filter. It's evaluated once and saved in a method for use in the future as-is.
With a lambda, however, the execution is held until the scope is actually applied, not until defined. It's necessary, because update_all
does have a side-effect.
The same trick is useful if you are specifying a scope with a constantly changing condition, the simplest case being dependency on Time.now
, that changes over time and needs to be re-evaluated each time.
However, I suggest you use a class method: a scope is more like a filter, whereas this is more like a "filter enforcement". While this might work too, scopes are semantically for different use cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With