Search code examples
ruby-on-railsrubydevisebelongs-toruby-on-rails-5

Rails: dependent: :destroy deletes record even after reassignment


I have a Rails 5 app where users can sign up as guest Users to try out my service. Each User (and guest user) can have many Apps. The relationship looks like this:

# User.rb
has_many :apps, dependent: :destroy

# App.rb
belongs_to :user

When a guest user decides to sign up to the service I want to:

  • (1) Reassign the guest user's app(s) to the new current user.
  • (2) Delete the old guest user (without deleting the app).

The problem I'm having is that the app gets deleted even if I reassign the app.user to my new current_user (I use the Device gem), before deleting the old guest user.

In my class Users::RegistrationsController < Devise::RegistrationsController I override the create action like this (the relevant parts):

# POST /resource
def create
  user_params = params[:user]
  new_user_from_guest = guest_user
  build_resource(sign_up_params)
  resource.save
  yield resource if block_given?
  if resource.persisted?
    if resource.active_for_authentication?
      set_flash_message :notice, :signed_up if is_flashing_format?
      sign_up(resource_name, resource)

      # Reassigns the app
      reasign_app_for_user(new_user_from_guest)

      Mailer.deliver_welcome_message(current_user).deliver_later!
      respond_with resource, location: after_sign_up_path_for(resource)

      # delete the guest user (this also deletes the app)
      delete_guest_user!
    else
      [...]
    end
  else
    [...]
  end
end

# ... my private methods for deleting the current user and reassigning the app's owner.
def reasign_app_for_user(user)
  user.apps.each do |app|
    app.user = current_user
    app.save!
  end
end

# ... this method deletes the app, even if it is called after the reassign method.
def delete_guest_user!
  guest_user(with_retry = false).try(:destroy)
  session[:guest_user_id] = nil
end

Any ideas on why this is happening or what I'm doing wrong?

Update

This is how my user_params variable look like after submit:

<ActionController::Parameters {"name"=>"Test user", "email"=>"test@example.com", "password"=>"testing"} permitted: false>

And this is how sign_up_params look:

{"email"=>"test@example.com", "password"=>"testing", "name"=>"Test"}

Solution

  • I would consider duplicating the apps before assigning them to the new user. This way, there's no confusion about if it's the same app or not.

    guest.apps.each do |app|
      current_user.apps << app.dup
    end
    

    Simply like this. The shovel operator ensures that the new, duplicated app is saved to the current_user object.

    Or even simpler and more compact:

    current_user.apps = guest.apps.map(&:dup)
    current_user.save