dependent: :discard
gsdean opened this issue · comments
Would be nice to be able to use dependent: :discard
on associations
For example
has_one :referral_code, ->{ kept }, dependent: :discard
As far as I understand it, that would require overriding some part of ActiveRecord, making this a non-starter for the project. If I am mistaken, please suggest an implementation. Otherwise, you can simply use callbacks.
do you have an example of what you mean for using callbacks? Right now I'm using
dependent: :destroy
on the association
and then overriding destroy
def destroy
discard
end
Overriding ActiveRecord methods can cause a fair bit of confusion when people aren't expecting it, so I wouldn't recommend your approach. That's partly why we refuse to do it in this gem. I would use either a before_destroy
or after_destroy
callback to discard the referral codes associated with the record.
You can find documentation about ActiveRecord callbacks here.
Sorry, are you suggesting calling discard
and returning false in before_destroy
?
I understand you don't want to override AR methods as part of the gem, but I believe that just forces everyone to either override them themselves as I did above, or something equivalent with a callback. Again sorry, if I'm missing something...
Just to clarify, this is the use case. Note: we're not destroying the user, just changing the association...
class ReferralCode < AR::Base
include Discard::Model
belongs_to :user
end
class User < AR::Base
has_many :referral_codes, dependent: :destroy # would like to use :discard
end
user.referral_codes = [] # would like this to result in soft delete
We're definitely not forcing anyone to override ActiveRecord methods. The thing you're missing/that I wasn't clear about is that the callback should be on the parent record. When you destroy the user, discard all the referral codes:
class ReferralCode < AR::Base
include Discard::Model
belongs_to :user
end
class User < AR::Base
has_many :referral_codes
after_destroy do # or before_destroy, depending on your specific needs
referral_codes.discard_all
end
end
Thanks for opening the issue and getting me to actually post the code that solves this. I'm sure it will be helpful to others.
Yes I understand the example you've provided. However, we're talking about different use cases. See my example above...
@jarednorman any additional thoughts here?
Ah sorry, I obviously didn't read all of that message, but I'm still a little confused. Getting user.referral_codes = []
to discard the referral codes has nothing to do with the dependent
option. Even if we added dependent: :discard
, that wouldn't make that happen. The dependent
option causes associated records to be destroyed when the parent is destroyed, i.e. if you destroy the user, the referral codes get destroyed too.
I would try to refactor your code to make the discarding of records explicit. That's kind of the point of discard: no magic. If you can't do that (and I understand why you might not be able to) you could override User#referral_codes=
, something like this might work:
def referral_codes=(other)
(referral_codes - other).each(&:discard)
self.referral_codes = other
end
I would try to avoid doing this though.
Agreed don't love this.
Seems like we may be misaligned on how dependent works. I realize the docs explicitly say when the owner is destroyed, but I believe it also is used when the association is removed (i.e. my examples above)...
No, that happens regardless. Sorry, I'm wrong about that. The docs here around collection.delete
point out that you're correct: https://www.rubydoc.info/docs/rails/4.1.7/ActiveRecord%2FAssociations%2FClassMethods:has_many
Well, thanks for teaching me a few things about how that all works. I was unaware that dependent:
changed how other operations on the association work. Despite my confusion about all that, I do think that my code snippet above is your best bet to get the behaviour you're looking for.