Allow cascade delete Unit/Quantity

Closes #32
This commit is contained in:
2026-02-22 17:50:43 +01:00
parent 1ba7d29441
commit 80130fb7d1
9 changed files with 19 additions and 17 deletions

View File

@@ -3,7 +3,8 @@ class Unit < ApplicationRecord
belongs_to :user, optional: true belongs_to :user, optional: true
belongs_to :base, optional: true, class_name: "Unit" belongs_to :base, optional: true, class_name: "Unit"
has_many :subunits, class_name: "Unit", inverse_of: :base, dependent: :restrict_with_error has_many :subunits, class_name: "Unit", inverse_of: :base,
dependent: :restrict_with_error
validate if: ->{ base.present? } do validate if: ->{ base.present? } do
errors.add(:base, :user_mismatch) unless user_id == base.user_id errors.add(:base, :user_mismatch) unless user_id == base.user_id

View File

@@ -12,10 +12,10 @@ class User < ApplicationRecord
disabled: 0, # administratively disallowed to sign in disabled: 0, # administratively disallowed to sign in
}, default: :active, validate: true }, default: :active, validate: true
has_many :readouts, dependent: :destroy has_many :readouts, dependent: :delete_all
accepts_nested_attributes_for :readouts accepts_nested_attributes_for :readouts
has_many :quantities, dependent: :destroy has_many :quantities, dependent: :delete_all
has_many :units, dependent: :destroy has_many :units, dependent: :delete_all
validates :email, presence: true, uniqueness: true, validates :email, presence: true, uniqueness: true,
length: {maximum: type_for_attribute(:email).limit} length: {maximum: type_for_attribute(:email).limit}

View File

@@ -5,7 +5,7 @@ class CreateUnits < ActiveRecord::Migration[7.0]
t.string :symbol, null: false, limit: 15 t.string :symbol, null: false, limit: 15
t.text :description t.text :description
t.decimal :multiplier, null: false, precision: 30, scale: 15, default: 1.0 t.decimal :multiplier, null: false, precision: 30, scale: 15, default: 1.0
t.references :base, foreign_key: {to_table: :units} t.references :base, foreign_key: {to_table: :units, on_delete: :cascade}
t.timestamps null: false t.timestamps null: false
end end

View File

@@ -4,7 +4,7 @@ class CreateQuantities < ActiveRecord::Migration[7.2]
t.references :user, foreign_key: true t.references :user, foreign_key: true
t.string :name, null: false, limit: 31 t.string :name, null: false, limit: 31
t.text :description t.text :description
t.references :parent, foreign_key: {to_table: :quantities} t.references :parent, foreign_key: {to_table: :quantities, on_delete: :cascade}
t.timestamps null: false t.timestamps null: false

View File

@@ -69,11 +69,11 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_21_230456) do
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end end
add_foreign_key "quantities", "quantities", column: "parent_id" add_foreign_key "quantities", "quantities", column: "parent_id", on_delete: :cascade
add_foreign_key "quantities", "users" add_foreign_key "quantities", "users"
add_foreign_key "readouts", "quantities" add_foreign_key "readouts", "quantities"
add_foreign_key "readouts", "units" add_foreign_key "readouts", "units"
add_foreign_key "readouts", "users" add_foreign_key "readouts", "users"
add_foreign_key "units", "units", column: "base_id" add_foreign_key "units", "units", column: "base_id", on_delete: :cascade
add_foreign_key "units", "users" add_foreign_key "units", "users"
end end

View File

@@ -1,5 +1,5 @@
Unit.transaction do Unit.transaction do
Unit.defaults.order(Unit.arel_table[:base_id].eq(nil)).delete_all Unit.defaults.delete_all
units = {} units = {}
<% Unit.defaults.ordered.each do |unit| %> <% Unit.defaults.ordered.each do |unit| %>
<%= "\n" if unit.base.nil? %> <%= "\n" if unit.base.nil? %>

View File

@@ -1,5 +1,5 @@
Unit.transaction do Unit.transaction do
Unit.defaults.order(Unit.arel_table[:base_id].eq(nil)).delete_all Unit.defaults.delete_all
units = {} units = {}

View File

@@ -6,14 +6,14 @@ require "application_system_test_case"
# * user with no units # * user with no units
class UnitsTest < ApplicationSystemTestCase class UnitsTest < ApplicationSystemTestCase
LINK_LABELS = { LINK_LABELS = {}
new_unit: t('units.index.new_unit'),
new_subunit: t('units.unit.new_subunit'),
edit: nil
}
setup do setup do
@user = sign_in @user = sign_in
LINK_LABELS.clear
LINK_LABELS[:new_unit] = t('units.index.new_unit')
LINK_LABELS[:new_subunit] = t('units.unit.new_subunit')
LINK_LABELS[:edit] = Regexp.union(@user.units.map(&:symbol)) LINK_LABELS[:edit] = Regexp.union(@user.units.map(&:symbol))
visit units_path visit units_path
@@ -26,7 +26,7 @@ class UnitsTest < ApplicationSystemTestCase
end end
# Cannot #destroy_all due to {dependent: :restrict*} on Unit.subunits association # Cannot #destroy_all due to {dependent: :restrict*} on Unit.subunits association
@user.units.order(Unit.arel_table[:base_id].eq(nil)).delete_all @user.units.delete_all
visit units_path visit units_path
within 'tbody' do within 'tbody' do
assert_selector 'tr', count: 1 assert_selector 'tr', count: 1

View File

@@ -157,9 +157,10 @@ class UsersTest < ApplicationSystemTestCase
end end
assert_difference ->{ User.count }, -1 do assert_difference ->{ User.count }, -1 do
accept_confirm { click_on t("users.registrations.edit.delete") } accept_confirm { click_on t("users.registrations.edit.delete") }
end
assert_current_path new_user_session_path assert_current_path new_user_session_path
end end
assert_text t("devise.registrations.destroyed")
end
test "index forbidden for non admin" do test "index forbidden for non admin" do
sign_in user: users.reject(&:admin?).select(&:confirmed?).sample sign_in user: users.reject(&:admin?).select(&:confirmed?).sample