diff --git a/config/application.rb.dist b/config/application.rb.dist index b54a357..9270b0c 100644 --- a/config/application.rb.dist +++ b/config/application.rb.dist @@ -18,6 +18,8 @@ require "rails/test_unit/railtie" # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) +require_relative '../lib/default_settings_strategy' + module FixinMe class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. @@ -39,6 +41,9 @@ module FixinMe config.action_dispatch.rescue_responses['ApplicationController::AccessForbidden'] = :forbidden config.action_dispatch.rescue_responses['ApplicationController::ParameterInvalid'] = :unprocessable_entity + # Set default migrations parameters. + config.active_record.migration_strategy = DefaultSettingsStrategy + # SETUP: Below settings need to be updated on a per-installation basis. # # Set host to be used by links generated in mailer templates. diff --git a/db/migrate/20230309185340_create_users.rb b/db/migrate/20230309185340_create_users.rb index 1efb52e..25f32c7 100644 --- a/db/migrate/20230309185340_create_users.rb +++ b/db/migrate/20230309185340_create_users.rb @@ -1,11 +1,11 @@ -class CreateUsers < ActiveRecord::Migration[7.0] +class CreateUsers < ActiveRecord::Migration[8.1] def change create_table :users do |t| t.string :email, null: false, limit: 64 t.integer :status, null: false, default: 0 - t.timestamps null: false + t.timestamps end - add_index :users, :email, unique: true + add_index :users, :email end end diff --git a/db/migrate/20230311220654_add_devise_to_users.rb b/db/migrate/20230311220654_add_devise_to_users.rb index 9ee005b..ad6c38d 100644 --- a/db/migrate/20230311220654_add_devise_to_users.rb +++ b/db/migrate/20230311220654_add_devise_to_users.rb @@ -1,4 +1,4 @@ -class AddDeviseToUsers < ActiveRecord::Migration[7.0] +class AddDeviseToUsers < ActiveRecord::Migration[8.1] def change change_table :users do |t| ## NOTE: commented fields left for reference/inclusion in future migrations @@ -34,8 +34,8 @@ class AddDeviseToUsers < ActiveRecord::Migration[7.0] # t.integer :failed_attempts, default: 0, null: false end - add_index :users, :reset_password_token, unique: true - add_index :users, :confirmation_token, unique: true + add_index :users, :reset_password_token + add_index :users, :confirmation_token # add_index :users, :unlock_token, unique: true end end diff --git a/db/migrate/20230602185352_create_units.rb b/db/migrate/20230602185352_create_units.rb index d615c97..2e7a9a6 100644 --- a/db/migrate/20230602185352_create_units.rb +++ b/db/migrate/20230602185352_create_units.rb @@ -1,4 +1,4 @@ -class CreateUnits < ActiveRecord::Migration[7.0] +class CreateUnits < ActiveRecord::Migration[8.1] def change create_table :units do |t| t.references :user, foreign_key: {on_delete: :cascade} @@ -7,8 +7,8 @@ class CreateUnits < ActiveRecord::Migration[7.0] t.decimal :multiplier, null: false, precision: 30, scale: 15, default: 1.0 t.references :base, foreign_key: {to_table: :units, on_delete: :cascade} - t.timestamps null: false + t.timestamps end - add_index :units, [:user_id, :symbol], unique: true + add_index :units, [:user_id, :symbol] end end diff --git a/db/migrate/20250104194343_create_quantities.rb b/db/migrate/20250104194343_create_quantities.rb index 59c98b5..1a70d32 100644 --- a/db/migrate/20250104194343_create_quantities.rb +++ b/db/migrate/20250104194343_create_quantities.rb @@ -1,4 +1,4 @@ -class CreateQuantities < ActiveRecord::Migration[7.2] +class CreateQuantities < ActiveRecord::Migration[8.1] def change create_table :quantities do |t| t.references :user, foreign_key: {on_delete: :cascade} @@ -6,12 +6,12 @@ class CreateQuantities < ActiveRecord::Migration[7.2] t.text :description t.references :parent, foreign_key: {to_table: :quantities, on_delete: :cascade} - t.timestamps null: false + t.timestamps # Caches; can be computed from other attributes t.integer :depth, null: false, default: 0 t.string :pathname, null: false, limit: 511 end - add_index :quantities, [:user_id, :parent_id, :name], unique: true + add_index :quantities, [:user_id, :parent_id, :name] end end diff --git a/db/migrate/20250121230456_create_readouts.rb b/db/migrate/20250121230456_create_readouts.rb index 25cf6ca..208f6f4 100644 --- a/db/migrate/20250121230456_create_readouts.rb +++ b/db/migrate/20250121230456_create_readouts.rb @@ -1,4 +1,4 @@ -class CreateReadouts < ActiveRecord::Migration[7.2] +class CreateReadouts < ActiveRecord::Migration[8.1] def change create_table :readouts do |t| t.references :user, null: false, foreign_key: {on_delete: :cascade} @@ -10,8 +10,7 @@ class CreateReadouts < ActiveRecord::Migration[7.2] #t.references :collector, foreign_key: true #t.references :device, foreign_key: true - t.timestamps null: false + t.timestamps end - add_index :readouts, [:quantity_id, :created_at], unique: true end end diff --git a/lib/default_settings_strategy.rb b/lib/default_settings_strategy.rb new file mode 100644 index 0000000..26cc12b --- /dev/null +++ b/lib/default_settings_strategy.rb @@ -0,0 +1,32 @@ +class DefaultSettingsStrategy < ActiveRecord::Migration::DefaultStrategy + # Without `:if_not_exists`/`:if_exists` options it's impossible to change + # migration status once migration fails partially. If `up` migration creates + # some, but not all objects, its status is not updated from `down` to `up`. + # Then it's impossible to migrate: a) up - due to `already exists` errors and + # b) down - due to migration status. + # Using `force: :cascade` does nothing on MySQL, so `force:` is useless. + # Adding `null: false` here somehow does not show up in schema files, be careful! + # TODO: add_foreign_key {on_delete: :cascade} ? + DEFAULTS = { + add_check_constraint: {if_not_exists: true}, + add_column: {if_not_exists: true}, + add_foreign_key: {if_not_exists: true}, + add_index: {if_not_exists: true, unique: true}, + # Timestamps are by default `null: false, precision: true`. + add_timestamps: {if_not_exists: true}, + create_table: {if_not_exists: true}, + drop_table: {if_exists: true}, + remove_check_constraint: {if_exists: true}, + remove_column: {if_exists: true}, + remove_foreign_key: {if_exists: true}, + remove_index: {if_exists: true}, + remove_timestamps: {if_exists: true}, + } + DEFAULTS.default = {} + DEFAULTS.freeze + + def method_missing(method, *args, **kwargs, &) + conflicts = kwargs.has_key?(:force) ? [:if_not_exists, :if_exists] : [] + super(method, *args, **DEFAULTS[method.to_sym].except(*conflicts).merge(kwargs), &) + end +end