jhawthorn / discard

🃏🗑 Soft deletes for ActiveRecord done right

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.