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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 900512ea1f780b0a9aa5f53e8bb08a627f45ebb99c3c78fbd295a69674029ea6
4
- data.tar.gz: 860191187fbe5d59b6a01eb08463a8530cd966edfb17305aef3f3d32f0fcea3c
3
+ metadata.gz: f119f92c66cd1a66e53b8fa3f23e1ebe816bb187100fa9c1f50e633042c038ad
4
+ data.tar.gz: 90d6339f1997bd3805fba30b7b252a437e000f00d81b86338276f30d38c5a6e8
5
5
  SHA512:
6
- metadata.gz: f77067d7ec9ffccc39aa3a9c7ce42b605e88ba4aaf139c1d6b71e7afaf29a5191c24950ff83a4839a7f880404cd5e842beb583c0311b8b70e5d30e228207fe4d
7
- data.tar.gz: 2b5e10af96721e262636d27fdee6efb984880debf90a179d514688918ec9cfb17e7b1c404c3f0b91d7dc398fa064f6aec5827954ee6eb75f08ab0c3cc3c3a7a6
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- safe_migrations (1.0.1)
4
+ safe_migrations (1.0.3)
5
5
  activerecord (>= 7.0)
6
6
 
7
7
  GEM
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
+ [![Gem Version](https://badge.fury.io/rb/safe_migrations.svg)](https://badge.fury.io/rb/safe_migrations)
6
+ [![CI](https://github.com/moskvin/safe_migrations/actions/workflows/ci.yml/badge.svg)](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.2+ Ready**: Built for ActiveRecord 7.2, with support for modern Ruby 3.2.
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 safe_change_column (needs type tracking)
56
- def invert_safe_change_column(args)
57
- table, column, type, options = args
58
- # If column existed, invert to original type (not tracked, so warn)
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
@@ -1,3 +1,3 @@
1
1
  module SafeMigrations
2
- VERSION = '1.0.1'.freeze
2
+ VERSION = '1.0.3'.freeze
3
3
  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.1
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-24 00:00:00.000000000 Z
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"