safe_migrations 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +102 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -1
- data/lib/safe_migrations/command_recorder_extension.rb +12 -7
- data/lib/safe_migrations/migration_helper.rb +28 -0
- data/lib/safe_migrations/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f119f92c66cd1a66e53b8fa3f23e1ebe816bb187100fa9c1f50e633042c038ad
|
|
4
|
+
data.tar.gz: 90d6339f1997bd3805fba30b7b252a437e000f00d81b86338276f30d38c5a6e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0416fe1c97cddc47a4f5dff0f317395e1979e5e8b2bcf75c8474b98d746760ad15505d2087dcb518982a51b27809161182403ef0934c8163de9575555b2582fb
|
|
7
|
+
data.tar.gz: 5e95b1bc908f4e7c4b1bcbc01d066e55239c689262b8a2ab41efc654734cf4b4d1bef1119b1ba3e229778a1b82352abf25cc335353971c90f812b01d50549b70
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
ruby: [3.2]
|
|
16
|
+
rails: ["7.0", "7.1"]
|
|
17
|
+
db: [sqlite, postgres, mysql, mariadb]
|
|
18
|
+
|
|
19
|
+
services:
|
|
20
|
+
postgres:
|
|
21
|
+
image: postgres:15
|
|
22
|
+
ports: [5432:5432]
|
|
23
|
+
env:
|
|
24
|
+
POSTGRES_USER: postgres
|
|
25
|
+
POSTGRES_PASSWORD: postgres
|
|
26
|
+
options: >-
|
|
27
|
+
--health-cmd "pg_isready -U postgres"
|
|
28
|
+
--health-interval 5s
|
|
29
|
+
--health-timeout 5s
|
|
30
|
+
--health-retries 5
|
|
31
|
+
|
|
32
|
+
mysql:
|
|
33
|
+
image: mysql:8
|
|
34
|
+
ports: [3306:3306]
|
|
35
|
+
env:
|
|
36
|
+
MYSQL_ROOT_PASSWORD: root
|
|
37
|
+
MYSQL_DATABASE: safe_migrations_test
|
|
38
|
+
options: >-
|
|
39
|
+
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
|
|
40
|
+
--health-interval=5s
|
|
41
|
+
--health-timeout=10s
|
|
42
|
+
--health-retries=5
|
|
43
|
+
|
|
44
|
+
mariadb:
|
|
45
|
+
image: mariadb:latest
|
|
46
|
+
ports: [3307:3306]
|
|
47
|
+
env:
|
|
48
|
+
MARIADB_ROOT_PASSWORD: root
|
|
49
|
+
MARIADB_DATABASE: safe_migrations_test
|
|
50
|
+
options: >-
|
|
51
|
+
--health-cmd="healthcheck.sh --connect"
|
|
52
|
+
--health-interval=5s
|
|
53
|
+
--health-timeout=10s
|
|
54
|
+
--health-retries=5
|
|
55
|
+
|
|
56
|
+
env:
|
|
57
|
+
RAILS_ENV: test
|
|
58
|
+
BUNDLE_JOBS: 4
|
|
59
|
+
BUNDLE_RETRY: 3
|
|
60
|
+
POSTGRES_USER: postgres
|
|
61
|
+
POSTGRES_PASSWORD: postgres
|
|
62
|
+
POSTGRES_DB: safe_migrations_test
|
|
63
|
+
MYSQL_ROOT_PASSWORD: root
|
|
64
|
+
MYSQL_DATABASE: safe_migrations_test
|
|
65
|
+
|
|
66
|
+
steps:
|
|
67
|
+
- name: Checkout repository
|
|
68
|
+
uses: actions/checkout@v4
|
|
69
|
+
|
|
70
|
+
- name: Set up Ruby
|
|
71
|
+
uses: ruby/setup-ruby@v1
|
|
72
|
+
with:
|
|
73
|
+
ruby-version: ${{ matrix.ruby }}
|
|
74
|
+
bundler-cache: true
|
|
75
|
+
|
|
76
|
+
- name: Install dependencies
|
|
77
|
+
run: |
|
|
78
|
+
gem install bundler
|
|
79
|
+
bundle add rails -v "~> ${{ matrix.rails }}" --skip-install
|
|
80
|
+
bundle install
|
|
81
|
+
|
|
82
|
+
- name: Prepare database
|
|
83
|
+
run: |
|
|
84
|
+
case ${{ matrix.db }} in
|
|
85
|
+
sqlite)
|
|
86
|
+
export DATABASE_URL="sqlite3::memory:"
|
|
87
|
+
;;
|
|
88
|
+
postgres)
|
|
89
|
+
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/safe_migrations_test"
|
|
90
|
+
;;
|
|
91
|
+
mysql)
|
|
92
|
+
export DATABASE_URL="mysql2://root:root@127.0.0.1:3306/safe_migrations_test"
|
|
93
|
+
;;
|
|
94
|
+
mariadb)
|
|
95
|
+
export DATABASE_URL="mysql2://root:root@127.0.0.1:3307/safe_migrations_test"
|
|
96
|
+
;;
|
|
97
|
+
esac
|
|
98
|
+
|
|
99
|
+
echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
|
|
100
|
+
|
|
101
|
+
- name: Run specs
|
|
102
|
+
run: bundle exec rspec
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
G'day, mate! Welcome to **SafeMigrations**, a ripper of a gem that makes your Rails migrations as safe as a kangaroo in the outback. With `safe_` prefixed methods, this gem ensures your database schema changes are idempotent—no dramas if you run 'em twice. Built to play nice with Rails' `CommandRecorder`, it auto-reverses your migrations in the `change` method, so you can crack on with building your app without worrying about dodgy rollbacks.
|
|
4
4
|
|
|
5
|
+
[](https://badge.fury.io/rb/safe_migrations)
|
|
6
|
+
[](https://github.com/moskvin/safe_migrations/actions)
|
|
7
|
+
|
|
5
8
|
## Why SafeMigrations?
|
|
6
9
|
|
|
7
10
|
Tired of migrations chucking a wobbly when tables or columns already exist? SafeMigrations wraps Rails migration methods with `safe_` prefixes (e.g., `safe_create_table`, `safe_add_column`) to check for existing schema elements before making changes. It hooks into Rails' `CommandRecorder` for automatic reversals in `change`-based migrations, keeping your database fair dinkum.
|
|
@@ -11,7 +14,7 @@ Tired of migrations chucking a wobbly when tables or columns already exist? Safe
|
|
|
11
14
|
|
|
12
15
|
- **Idempotent Migrations**: `safe_create_table`, `safe_add_column`, `safe_add_index`, and more only run if needed.
|
|
13
16
|
- **Auto-Reversal**: Integrates with Rails' `CommandRecorder` to invert `safe_` methods (e.g., `safe_create_table` → `safe_drop_table`) in `change` rollbacks.
|
|
14
|
-
- **Rails 7.
|
|
17
|
+
- **Rails 7.0+ Ready**: Built for ActiveRecord 7.0, with support for modern Ruby 3.2.
|
|
15
18
|
- **Aussie Spirit**: Crafted with a bit of outback grit to keep your migrations smooth as a cold one on a summer arvo.
|
|
16
19
|
|
|
17
20
|
## Installation
|
|
@@ -16,6 +16,10 @@ module SafeMigrations
|
|
|
16
16
|
safe_remove_column_and_index
|
|
17
17
|
safe_change_column
|
|
18
18
|
safe_change_column_null
|
|
19
|
+
safe_add_reference
|
|
20
|
+
safe_remove_reference
|
|
21
|
+
safe_add_check_constraint
|
|
22
|
+
safe_remove_check_constraint
|
|
19
23
|
].freeze
|
|
20
24
|
|
|
21
25
|
SAFE_REVERSIBLE_MAP = {
|
|
@@ -28,7 +32,10 @@ module SafeMigrations
|
|
|
28
32
|
safe_remove_foreign_key: :safe_add_foreign_key,
|
|
29
33
|
safe_add_column_and_index: :safe_remove_column_and_index,
|
|
30
34
|
safe_remove_column_and_index: :safe_add_column_and_index,
|
|
31
|
-
safe_change_column_null: :safe_change_column_null
|
|
35
|
+
safe_change_column_null: :safe_change_column_null,
|
|
36
|
+
safe_rename_column: :safe_rename_column,
|
|
37
|
+
safe_add_reference: :safe_remove_reference,
|
|
38
|
+
safe_add_check_constraint: :safe_remove_check_constraint
|
|
32
39
|
}.freeze
|
|
33
40
|
|
|
34
41
|
def self.apply
|
|
@@ -52,12 +59,10 @@ module SafeMigrations
|
|
|
52
59
|
[:safe_rename_column, [table, new_col, old_col]]
|
|
53
60
|
end
|
|
54
61
|
|
|
55
|
-
# Special case for
|
|
56
|
-
def
|
|
57
|
-
table, column,
|
|
58
|
-
|
|
59
|
-
# If column was added, invert to remove
|
|
60
|
-
[:safe_remove_column, [table, column, type, options]]
|
|
62
|
+
# Special case for safe_change_column_null (needs invert value)
|
|
63
|
+
def invert_safe_change_column_null(args)
|
|
64
|
+
table, column, value = args
|
|
65
|
+
[:safe_change_column_null, [table, column, !value]]
|
|
61
66
|
end
|
|
62
67
|
end
|
|
63
68
|
end
|
|
@@ -67,6 +67,34 @@ module SafeMigrations
|
|
|
67
67
|
def safe_remove_foreign_key(from_table, to_table, **options)
|
|
68
68
|
table_exists?(from_table) && table_exists?(to_table) && foreign_key_exists?(from_table, to_table, **options) && remove_foreign_key(from_table, to_table, **options)
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
def safe_add_reference(table, ref_name, **)
|
|
72
|
+
return unless table_exists?(table)
|
|
73
|
+
|
|
74
|
+
column_exists?(table, "#{ref_name.to_s.singularize}_id") || add_reference(table, ref_name, **)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def safe_remove_reference(table, ref_name, **)
|
|
78
|
+
table_exists?(table) && column_exists?(table, "#{ref_name.to_s.singularize}_id") && remove_reference(table, ref_name, **)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def check_constraint_exists?(table_name, **options)
|
|
82
|
+
if !options.key?(:name) && !options.key?(:expression)
|
|
83
|
+
raise ArgumentError, 'At least one of :name or :expression must be supplied'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
check_constraint_for(table_name, **options).present?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def safe_add_check_constraint(table, condition, name:, **)
|
|
90
|
+
return unless table_exists?(table)
|
|
91
|
+
|
|
92
|
+
check_constraint_exists?(table, name:) || add_check_constraint(table, condition, name:, **)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def safe_remove_check_constraint(table, condition, name:, **)
|
|
96
|
+
table_exists?(table) && check_constraint_exists?(table, name:) && remove_check_constraint(table, condition, name:, **)
|
|
97
|
+
end
|
|
70
98
|
end
|
|
71
99
|
end
|
|
72
100
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: safe_migrations
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nikolay Moskvin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -90,6 +90,7 @@ executables: []
|
|
|
90
90
|
extensions: []
|
|
91
91
|
extra_rdoc_files: []
|
|
92
92
|
files:
|
|
93
|
+
- ".github/workflows/ci.yml"
|
|
93
94
|
- ".gitignore"
|
|
94
95
|
- ".rspec"
|
|
95
96
|
- ".rubocop.yml"
|