rubocop-migration 0.8.0 → 0.9.0
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/Gemfile.lock +1 -1
- data/lib/rubocop/cop/migration/add_check_constraint.rb +14 -1
- data/lib/rubocop/cop/migration/add_column_with_default_value.rb +12 -0
- data/lib/rubocop/cop/migration/add_index_columns_count.rb +1 -1
- data/lib/rubocop/cop/migration/add_index_concurrently.rb +23 -1
- data/lib/rubocop/cop/migration/change_column_null.rb +40 -2
- data/lib/rubocop/cop/migration/remove_column.rb +24 -5
- data/lib/rubocop/migration/cop_concerns/new_table.rb +80 -0
- data/lib/rubocop/migration/cop_concerns.rb +1 -0
- data/lib/rubocop/migration/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz: '
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '04080713782c1efcf724f467e77e9e550d70070bcb1d69592caeb1d4740b9282'
|
|
4
|
+
data.tar.gz: 80fece0029a643072003348334363a51a84c636fdcf2c50219a3b8ca967f25f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb8d8c1f01f4034d17c474cb8e2561a5a0b74b34aef294f464891619fabed9f8210653d19c20115cb7a5883ca141054244c13d1fa26e258f489ef14c0365b569
|
|
7
|
+
data.tar.gz: db392a7a94d3857e1db00fa59dad6739d6c8292bfcffccd71c2684c98a80332afe36f90c452cd4ed5ed48cba5ce27f48129ffc3d87a9969658131078b7e750bc
|
data/Gemfile.lock
CHANGED
|
@@ -31,9 +31,21 @@ module RuboCop
|
|
|
31
31
|
# validate_check_constraint :orders, name: 'orders_price_positive'
|
|
32
32
|
# end
|
|
33
33
|
# end
|
|
34
|
+
#
|
|
35
|
+
# # good
|
|
36
|
+
# class CreateOrders < ActiveRecord::Migration[7.0]
|
|
37
|
+
# def change
|
|
38
|
+
# create_table :orders do |t|
|
|
39
|
+
# t.integer :price
|
|
40
|
+
# end
|
|
41
|
+
# add_check_constraint :orders, 'price > 0', name: 'orders_price_positive'
|
|
42
|
+
# end
|
|
43
|
+
# end
|
|
34
44
|
class AddCheckConstraint < RuboCop::Cop::Base
|
|
35
45
|
extend AutoCorrector
|
|
36
46
|
|
|
47
|
+
include ::RuboCop::Migration::CopConcerns::NewTable
|
|
48
|
+
|
|
37
49
|
MSG = 'Activate a check constraint in a separate migration in PostgreSQL.'
|
|
38
50
|
|
|
39
51
|
RESTRICT_ON_SEND = %i[
|
|
@@ -104,7 +116,8 @@ module RuboCop
|
|
|
104
116
|
# @return [Boolean]
|
|
105
117
|
def bad?(node)
|
|
106
118
|
add_check_constraint?(node) &&
|
|
107
|
-
!add_check_constraint_with_validate_false?(node)
|
|
119
|
+
!add_check_constraint_with_validate_false?(node) &&
|
|
120
|
+
!new_table?(node.first_argument)
|
|
108
121
|
end
|
|
109
122
|
end
|
|
110
123
|
end
|
|
@@ -27,11 +27,22 @@ module RuboCop
|
|
|
27
27
|
# change_column_default :users, :some_column, 'some value'
|
|
28
28
|
# end
|
|
29
29
|
# end
|
|
30
|
+
#
|
|
31
|
+
# # good
|
|
32
|
+
# class CreateUsers < ActiveRecord::Migration[7.0]
|
|
33
|
+
# def change
|
|
34
|
+
# create_table :users do |t|
|
|
35
|
+
# t.string :name
|
|
36
|
+
# end
|
|
37
|
+
# add_column :users, :some_column, :string, default: 'some value'
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
30
40
|
class AddColumnWithDefaultValue < RuboCop::Cop::Base
|
|
31
41
|
extend AutoCorrector
|
|
32
42
|
|
|
33
43
|
include RangeHelp
|
|
34
44
|
include ::RuboCop::Migration::CopConcerns::ColumnTypeMethod
|
|
45
|
+
include ::RuboCop::Migration::CopConcerns::NewTable
|
|
35
46
|
|
|
36
47
|
MSG = 'Add the column without a default value then change the default.'
|
|
37
48
|
|
|
@@ -44,6 +55,7 @@ module RuboCop
|
|
|
44
55
|
# @return [void]
|
|
45
56
|
def on_send(node)
|
|
46
57
|
return unless target_method?(node)
|
|
58
|
+
return if new_table?(find_table_node_from(node))
|
|
47
59
|
|
|
48
60
|
default_option_node = non_nil_default_option_node_from(node)
|
|
49
61
|
return unless default_option_node
|
|
@@ -26,10 +26,21 @@ module RuboCop
|
|
|
26
26
|
# add_index :users, :name, algorithm: :concurrently
|
|
27
27
|
# end
|
|
28
28
|
# end
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# class CreateUsers < ActiveRecord::Migration[7.0]
|
|
32
|
+
# def change
|
|
33
|
+
# create_table :users do |t|
|
|
34
|
+
# t.string :name
|
|
35
|
+
# end
|
|
36
|
+
# add_index :users, :name
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
29
39
|
class AddIndexConcurrently < RuboCop::Cop::Base
|
|
30
40
|
extend AutoCorrector
|
|
31
41
|
|
|
32
42
|
include ::RuboCop::Migration::CopConcerns::DisableDdlTransaction
|
|
43
|
+
include ::RuboCop::Migration::CopConcerns::NewTable
|
|
33
44
|
include RangeHelp
|
|
34
45
|
|
|
35
46
|
MSG = 'Use `algorithm: :concurrently` on adding indexes to existing tables in PostgreSQL.'
|
|
@@ -44,7 +55,7 @@ module RuboCop
|
|
|
44
55
|
# @param node [RuboCop::AST::SendNode]
|
|
45
56
|
# @return [void]
|
|
46
57
|
def on_send(node)
|
|
47
|
-
if add_index_without_concurrency?(node)
|
|
58
|
+
if add_index_without_concurrency?(node) && !new_table?(find_table_node_from(node))
|
|
48
59
|
add_offense(node) do |corrector|
|
|
49
60
|
autocorrect(corrector, node)
|
|
50
61
|
end
|
|
@@ -159,6 +170,17 @@ module RuboCop
|
|
|
159
170
|
disable_ddl_transactions_from(node)[1..] || []
|
|
160
171
|
end
|
|
161
172
|
|
|
173
|
+
# @param node [RuboCop::AST::SendNode]
|
|
174
|
+
# @return [RuboCop::AST::Node, nil]
|
|
175
|
+
def find_table_node_from(node)
|
|
176
|
+
case node.method_name
|
|
177
|
+
when :add_index
|
|
178
|
+
node.first_argument
|
|
179
|
+
else
|
|
180
|
+
node.each_ancestor(:block).first.send_node.first_argument
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
162
184
|
# @param node [RuboCop::AST::SendNode]
|
|
163
185
|
# @return [Boolean]
|
|
164
186
|
def in_change_table?(node)
|
|
@@ -28,16 +28,33 @@ module RuboCop
|
|
|
28
28
|
#
|
|
29
29
|
# class ReplaceNotNullConstraintOnUsersName < ActiveRecord::Migration[7.0]
|
|
30
30
|
# def change
|
|
31
|
-
#
|
|
31
|
+
# validate_check_constraint :users, name: 'users_name_is_not_null'
|
|
32
32
|
# change_column_null :users, :name, false
|
|
33
33
|
# remove_check_constraint :users, name: 'users_name_is_not_null'
|
|
34
34
|
# end
|
|
35
35
|
# end
|
|
36
|
+
#
|
|
37
|
+
# # bad
|
|
38
|
+
# class AllowNullOnUsersName < ActiveRecord::Migration[7.0]
|
|
39
|
+
# def change
|
|
40
|
+
# change_column_null :users, :name, true
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# # good
|
|
45
|
+
# class AllowNullOnUsersName < ActiveRecord::Migration[7.0]
|
|
46
|
+
# def up
|
|
47
|
+
# change_column_null :users, :name, true
|
|
48
|
+
# end
|
|
49
|
+
# end
|
|
36
50
|
class ChangeColumnNull < RuboCop::Cop::Base
|
|
37
51
|
extend AutoCorrector
|
|
38
52
|
|
|
39
53
|
MSG = 'Avoid simply setting `NOT NULL` constraint on an existing column in PostgreSQL.'
|
|
40
54
|
|
|
55
|
+
MSG_FOR_ALLOW_NULL_IN_CHANGE = 'Allow NULL in `up` instead of `change`, ' \
|
|
56
|
+
'as rolling back `change` simply sets `NOT NULL` constraint again.'
|
|
57
|
+
|
|
41
58
|
RESTRICT_ON_SEND = %i[
|
|
42
59
|
change_column_null
|
|
43
60
|
change_null
|
|
@@ -48,6 +65,11 @@ module RuboCop
|
|
|
48
65
|
def on_send(node)
|
|
49
66
|
return if called_with_validate_constraint?(node)
|
|
50
67
|
|
|
68
|
+
if allow_null?(node)
|
|
69
|
+
add_offense(node, message: MSG_FOR_ALLOW_NULL_IN_CHANGE) if in_change_method?(node)
|
|
70
|
+
return
|
|
71
|
+
end
|
|
72
|
+
|
|
51
73
|
add_offense(node) do |corrector|
|
|
52
74
|
autocorrect(corrector, node)
|
|
53
75
|
end
|
|
@@ -56,11 +78,21 @@ module RuboCop
|
|
|
56
78
|
|
|
57
79
|
private
|
|
58
80
|
|
|
81
|
+
# @!method allow_null?(node)
|
|
82
|
+
# @param node [RuboCop::AST::SendNode]
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def_node_matcher :allow_null?, <<~PATTERN
|
|
85
|
+
{
|
|
86
|
+
(send nil? :change_column_null _ _ true ...)
|
|
87
|
+
(call _ :change_null _ true ...)
|
|
88
|
+
}
|
|
89
|
+
PATTERN
|
|
90
|
+
|
|
59
91
|
# @!method validate_constraint?(node)
|
|
60
92
|
# @param node [RuboCop::AST::SendNode]
|
|
61
93
|
# @return [Boolean]
|
|
62
94
|
def_node_matcher :validate_constraint?, <<~PATTERN
|
|
63
|
-
(send nil? :validate_constraint ...)
|
|
95
|
+
(send nil? {:validate_constraint :validate_check_constraint} ...)
|
|
64
96
|
PATTERN
|
|
65
97
|
|
|
66
98
|
# @param corrector [RuboCop::Cop::Corrector]
|
|
@@ -203,6 +235,12 @@ module RuboCop
|
|
|
203
235
|
constraint_name: "#{table_name}_#{column_name}_is_not_null"
|
|
204
236
|
)
|
|
205
237
|
end
|
|
238
|
+
|
|
239
|
+
# @param node [RuboCop::AST::SendNode]
|
|
240
|
+
# @return [Boolean]
|
|
241
|
+
def in_change_method?(node)
|
|
242
|
+
node.each_ancestor(:def).first&.method?(:change) || false
|
|
243
|
+
end
|
|
206
244
|
end
|
|
207
245
|
end
|
|
208
246
|
end
|
|
@@ -121,9 +121,10 @@ module RuboCop
|
|
|
121
121
|
# @return [Array<String>]
|
|
122
122
|
def find_ignored_column_names_from(table_name)
|
|
123
123
|
pathname = model_pathname_from(
|
|
124
|
-
|
|
124
|
+
directory: ::Pathname.new('app/models'),
|
|
125
|
+
snake_cased_model_name: singularize(table_name)
|
|
125
126
|
)
|
|
126
|
-
return [] unless pathname
|
|
127
|
+
return [] unless pathname
|
|
127
128
|
|
|
128
129
|
ignored_column_nodes = ignored_column_nodes_from(
|
|
129
130
|
parse(
|
|
@@ -155,10 +156,28 @@ module RuboCop
|
|
|
155
156
|
)
|
|
156
157
|
end
|
|
157
158
|
|
|
159
|
+
# @note Tries `accounts_user.rb` first, then namespaces such as `accounts/user.rb`.
|
|
160
|
+
# @param directory [Pathname]
|
|
158
161
|
# @param snake_cased_model_name [String]
|
|
159
|
-
# @return [Pathname]
|
|
160
|
-
def model_pathname_from(
|
|
161
|
-
|
|
162
|
+
# @return [Pathname, nil]
|
|
163
|
+
def model_pathname_from(
|
|
164
|
+
directory:,
|
|
165
|
+
snake_cased_model_name:
|
|
166
|
+
)
|
|
167
|
+
pathname = directory.join("#{snake_cased_model_name}.rb")
|
|
168
|
+
return pathname if pathname.exist?
|
|
169
|
+
|
|
170
|
+
snake_cased_model_name.scan(/(?<=[^_])_(?=[^_])/) do
|
|
171
|
+
namespace = ::Regexp.last_match.pre_match
|
|
172
|
+
next unless directory.join(namespace).directory?
|
|
173
|
+
|
|
174
|
+
pathname = model_pathname_from(
|
|
175
|
+
directory: directory.join(namespace),
|
|
176
|
+
snake_cased_model_name: ::Regexp.last_match.post_match
|
|
177
|
+
)
|
|
178
|
+
return pathname if pathname
|
|
179
|
+
end
|
|
180
|
+
nil
|
|
162
181
|
end
|
|
163
182
|
|
|
164
183
|
# @param content [String]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Migration
|
|
5
|
+
module CopConcerns
|
|
6
|
+
module NewTable
|
|
7
|
+
class << self
|
|
8
|
+
def included(klass)
|
|
9
|
+
super
|
|
10
|
+
klass.class_eval do
|
|
11
|
+
# @!method create_table_if_not_exists?(node)
|
|
12
|
+
# @param node [RuboCop::AST::SendNode]
|
|
13
|
+
# @return [Boolean]
|
|
14
|
+
def_node_matcher :create_table_if_not_exists?, <<~PATTERN
|
|
15
|
+
(send
|
|
16
|
+
nil?
|
|
17
|
+
:create_table
|
|
18
|
+
_
|
|
19
|
+
(hash
|
|
20
|
+
<
|
|
21
|
+
(pair
|
|
22
|
+
(sym :if_not_exists)
|
|
23
|
+
true
|
|
24
|
+
)
|
|
25
|
+
...
|
|
26
|
+
>
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
PATTERN
|
|
30
|
+
|
|
31
|
+
# @!method create_table_name_from(node)
|
|
32
|
+
# @param node [RuboCop::AST::SendNode]
|
|
33
|
+
# @return [String, Symbol, nil]
|
|
34
|
+
def_node_matcher :create_table_name_from, <<~PATTERN
|
|
35
|
+
(send
|
|
36
|
+
nil?
|
|
37
|
+
:create_table
|
|
38
|
+
({str sym} $_)
|
|
39
|
+
...
|
|
40
|
+
)
|
|
41
|
+
PATTERN
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# @param node [RuboCop::AST::Node]
|
|
49
|
+
# @param table_name [String]
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def create_table_of?(
|
|
52
|
+
node,
|
|
53
|
+
table_name
|
|
54
|
+
)
|
|
55
|
+
node = node.send_node if node.block_type?
|
|
56
|
+
node.send_type? &&
|
|
57
|
+
create_table_name_from(node)&.to_s == table_name &&
|
|
58
|
+
!create_table_if_not_exists?(node)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether the table is created by a `create_table` written directly in the method body before it,
|
|
62
|
+
# not under a condition, a block, or a rescue.
|
|
63
|
+
# @param table_node [RuboCop::AST::Node, nil]
|
|
64
|
+
# @return [Boolean]
|
|
65
|
+
def new_table?(table_node)
|
|
66
|
+
return false unless table_node&.type?(:str, :sym)
|
|
67
|
+
|
|
68
|
+
body_node = table_node.each_ancestor(:def, :defs).first&.body
|
|
69
|
+
return false unless body_node&.begin_type?
|
|
70
|
+
|
|
71
|
+
table_name = table_node.value.to_s
|
|
72
|
+
body_node.each_child_node.any? do |statement_node|
|
|
73
|
+
statement_node.source_range.end_pos < table_node.source_range.begin_pos &&
|
|
74
|
+
create_table_of?(statement_node, table_name)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -6,6 +6,7 @@ module RuboCop
|
|
|
6
6
|
autoload :BatchProcessing, 'rubocop/migration/cop_concerns/batch_processing'
|
|
7
7
|
autoload :ColumnTypeMethod, 'rubocop/migration/cop_concerns/column_type_method'
|
|
8
8
|
autoload :DisableDdlTransaction, 'rubocop/migration/cop_concerns/disable_ddl_transaction'
|
|
9
|
+
autoload :NewTable, 'rubocop/migration/cop_concerns/new_table'
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-migration
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryo Nakamura
|
|
@@ -104,6 +104,7 @@ files:
|
|
|
104
104
|
- lib/rubocop/migration/cop_concerns/batch_processing.rb
|
|
105
105
|
- lib/rubocop/migration/cop_concerns/column_type_method.rb
|
|
106
106
|
- lib/rubocop/migration/cop_concerns/disable_ddl_transaction.rb
|
|
107
|
+
- lib/rubocop/migration/cop_concerns/new_table.rb
|
|
107
108
|
- lib/rubocop/migration/plugin.rb
|
|
108
109
|
- lib/rubocop/migration/version.rb
|
|
109
110
|
- rubocop-migration.gemspec
|