Rails 5 before_destroy throw(:abort)
Asked Answered
H

2

8

I'm working in a legacy Rails app that has been diligently upgraded with each major version of Rails and we're currently on rails 5.1 and I can't get a before_destroy to prevent the deletion if it fails a validation

I've been reading that return false has been deprecated and we're all supposed to use throw :abort but neither are working. I'm not getting any errors, the join record just gets deleted despite the throw

user.rb:

class User < ApplicationRecord
  has_many :permission_users
  has_many :permissions, through: :permission_users, dependent: :destroy
end

join model permission_user.rb:

class PermissionUser < ApplicationRecord
  belongs_to :user
  belongs_to :permission

  before_destroy :check_before_removing!

  private

  def check_before_removing!
    if not_valid? # condition isn't important
      errors.add :base, exception.message
      throw(:abort)
    end
  end
end

my spec:

specify "cannot have their admin permissions revoked" do
  expect {
    admin.permissions.delete admin_permission
  }.to change { admin.permissions.count }.by(0)       
end

# => expected `admin.permissions.count` to have changed by 0, but was changed by -1
Hermaphroditism answered 28/6, 2018 at 2:29 Comment(2)
Bit off-topic, but: expect{}.to change{}.by(0) -> expect{}.not_to change{}Autostability
I actually trimmed down the spec to focus on the problem. I'm actually testing that 2 different values don't change and apparently rspec doesn't like the .to_not change { ... }.and change { ... } but thank youHermaphroditism
A
7

There's a catch with this callback:

before_destroy callbacks should be placed before dependent: :destroy associations (or use the prepend: true option), to ensure they execute before the records are deleted by dependent: :destroy.

So please try

before_destroy :check_before_removing!, prepend: true
Autostability answered 28/6, 2018 at 13:5 Comment(1)
unless I'm doing something wrong, this still isn't working. Same failure, still getting to the throw(:abort) and a logging after the throw doesn't get printed out so I know it's throwing or whatever at that pointHermaphroditism
S
5

try to do something like this

before_destroy :check_before_removing, prepend: true

def check_before_removing if true && some_condition throw(:abort) end end

Sandalwood answered 11/6, 2020 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.