sevencop 0.22.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +0 -16
  4. data/config/default.yml +0 -143
  5. data/lib/sevencop/cop_concerns.rb +0 -3
  6. data/lib/sevencop/version.rb +1 -1
  7. data/lib/sevencop.rb +0 -16
  8. metadata +2 -21
  9. data/lib/rubocop/cop/sevencop/rails_migration_add_check_constraint.rb +0 -111
  10. data/lib/rubocop/cop/sevencop/rails_migration_add_column_with_default_value.rb +0 -229
  11. data/lib/rubocop/cop/sevencop/rails_migration_add_foreign_key.rb +0 -166
  12. data/lib/rubocop/cop/sevencop/rails_migration_add_index_concurrently.rb +0 -164
  13. data/lib/rubocop/cop/sevencop/rails_migration_batch_in_batches.rb +0 -95
  14. data/lib/rubocop/cop/sevencop/rails_migration_batch_in_transaction.rb +0 -83
  15. data/lib/rubocop/cop/sevencop/rails_migration_batch_with_throttling.rb +0 -108
  16. data/lib/rubocop/cop/sevencop/rails_migration_change_column.rb +0 -113
  17. data/lib/rubocop/cop/sevencop/rails_migration_change_column_null.rb +0 -128
  18. data/lib/rubocop/cop/sevencop/rails_migration_create_table_force.rb +0 -89
  19. data/lib/rubocop/cop/sevencop/rails_migration_jsonb.rb +0 -131
  20. data/lib/rubocop/cop/sevencop/rails_migration_remove_column.rb +0 -258
  21. data/lib/rubocop/cop/sevencop/rails_migration_rename_column.rb +0 -81
  22. data/lib/rubocop/cop/sevencop/rails_migration_rename_table.rb +0 -79
  23. data/lib/rubocop/cop/sevencop/rails_migration_reserved_word_mysql.rb +0 -232
  24. data/lib/rubocop/cop/sevencop/rails_migration_unique_index_columns_count.rb +0 -92
  25. data/lib/sevencop/cop_concerns/batch_processing.rb +0 -32
  26. data/lib/sevencop/cop_concerns/column_type_method.rb +0 -26
  27. data/lib/sevencop/cop_concerns/disable_ddl_transaction.rb +0 -49
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Sevencop
6
- # Avoid changing column type that is in use.
7
- #
8
- # Changing the type of a column causes the entire table to be rewritten.
9
- # During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
10
- #
11
- # Some changes don’t require a table rewrite and are safe in PostgreSQL:
12
- #
13
- # Type | Safe Changes
14
- # --- | ---
15
- # `cidr` | Changing to `inet`
16
- # `citext` | Changing to `text` if not indexed, changing to `string` with no `:limit` if not indexed
17
- # `datetime` | Increasing or removing `:precision`, changing to `timestamptz` when session time zone is UTC in Postgres 12+
18
- # `decimal` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
19
- # `interval` | Increasing or removing `:precision`
20
- # `numeric` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
21
- # `string` | Increasing or removing `:limit`, changing to `text`, changing `citext` if not indexed
22
- # `text` | Changing to `string` with no `:limit`, changing to `citext` if not indexed
23
- # `time` | Increasing or removing `:precision`
24
- # `timestamptz` | Increasing or removing `:limit`, changing to `datetime` when session time zone is UTC in Postgres 12+
25
- #
26
- # And some in MySQL and MariaDB:
27
- #
28
- # Type | Safe Changes
29
- # --- | ---
30
- # `string` | Increasing `:limit` from under 255 up to 255, increasing `:limit` from over 255 to the max
31
- #
32
- # A safer approach is to:
33
- #
34
- # 1. Create a new column
35
- # 2. Write to both columns
36
- # 3. Backfill data from the old column to the new column
37
- # 4. Move reads from the old column to the new column
38
- # 5. Stop writing to the old column
39
- # 6. Drop the old column
40
- #
41
- # @safety
42
- # Only meaningful if the table is in use and the type change is really unsafe as described above.
43
- #
44
- # @example
45
- # # bad
46
- # class ChangeUsersSomeColumnType < ActiveRecord::Migration[7.0]
47
- # def change
48
- # change_column :users, :some_column, :new_type
49
- # end
50
- # end
51
- #
52
- # # good
53
- # class AddUsersAnotherColumn < ActiveRecord::Migration[7.0]
54
- # def change
55
- # add_column :users, :another_column, :new_type
56
- # end
57
- # end
58
- #
59
- # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
60
- # def change
61
- # remove_column :users, :some_column
62
- # end
63
- # end
64
- class RailsMigrationChangeColumn < RuboCop::Cop::Base
65
- MSG = 'Avoid changing column type that is in use.'
66
-
67
- RESTRICT_ON_SEND = %i[
68
- change
69
- change_column
70
- ].freeze
71
-
72
- # @param node [RuboCop::AST::SendNode]
73
- # @return [void]
74
- def on_send(node)
75
- return unless bad?(node)
76
-
77
- add_offense(node)
78
- end
79
-
80
- private
81
-
82
- # @!method change?(node)
83
- # @param node [RuboCop::AST::SendNode]
84
- # @return [Boolean]
85
- def_node_matcher :change?, <<~PATTERN
86
- (send
87
- lvar
88
- :change
89
- ...
90
- )
91
- PATTERN
92
-
93
- # @!method change_column?(node)
94
- # @param node [RuboCop::AST::SendNode]
95
- # @return [Boolean]
96
- def_node_matcher :change_column?, <<~PATTERN
97
- (send
98
- nil?
99
- :change_column
100
- ...
101
- )
102
- PATTERN
103
-
104
- # @param node [RuboCop::AST::SendNode]
105
- # @return [Boolean]
106
- def bad?(node)
107
- change?(node) ||
108
- change_column?(node)
109
- end
110
- end
111
- end
112
- end
113
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Sevencop
6
- # Avoid simply setting `NOT NULL` constraint on an existing column in PostgreSQL.
7
- #
8
- # It blocks reads and writes while every row is checked.
9
- # In PostgreSQL 12+, you can safely set `NOT NULL` constraint if corresponding check constraint exists.
10
- #
11
- # @safety
12
- # Only meaningful in PostgreSQL 12+.
13
- #
14
- # @example
15
- # # bad
16
- # class SetNotNullColumnConstraintToUsersName < ActiveRecord::Migration[7.0]
17
- # def change
18
- # change_column_null :users, :name, false
19
- # end
20
- # end
21
- #
22
- # # good
23
- # class SetNotNullCheckConstraintToUsersName < ActiveRecord::Migration[7.0]
24
- # def change
25
- # add_check_constraint :users, 'name IS NOT NULL', name: 'users_name_is_not_null', validate: false
26
- # end
27
- # end
28
- #
29
- # class ReplaceNotNullConstraintOnUsersName < ActiveRecord::Migration[7.0]
30
- # def change
31
- # validate_constraint :users, name: 'users_name_is_not_null'
32
- # change_column_null :users, :name, false
33
- # remove_check_constraint :users, name: 'users_name_is_not_null'
34
- # end
35
- # end
36
- class RailsMigrationChangeColumnNull < RuboCop::Cop::Base
37
- extend AutoCorrector
38
-
39
- MSG = 'Avoid simply setting `NOT NULL` constraint on an existing column in PostgreSQL.'
40
-
41
- RESTRICT_ON_SEND = %i[
42
- change_column_null
43
- ].freeze
44
-
45
- # @param node [RuboCop::AST::SendNode]
46
- # @return [void]
47
- def on_send(node)
48
- return if in_second_migration?(node)
49
-
50
- add_offense(node) do |corrector|
51
- autocorrect(corrector, node)
52
- end
53
- end
54
-
55
- private
56
-
57
- # @!method parse_table_name_and_column_name(node)
58
- # @param node [RuboCop::AST::SendNode]
59
- # @return [Array<Symbol>, nil]
60
- def_node_matcher :parse_table_name_and_column_name, <<~PATTERN
61
- (send
62
- nil?
63
- _
64
- ({str sym} $_)
65
- ({str sym} $_)
66
- ...
67
- )
68
- PATTERN
69
-
70
- # @!method remove_check_constraint?(node)
71
- # @param node [RuboCop::AST::SendNode]
72
- # @return [Boolean]
73
- def_node_matcher :remove_check_constraint?, <<~PATTERN
74
- (send nil? :remove_check_constraint ...)
75
- PATTERN
76
-
77
- # @!method validate_constraint?(node)
78
- # @param node [RuboCop::AST::SendNode]
79
- # @return [Boolean]
80
- def_node_matcher :validate_constraint?, <<~PATTERN
81
- (send nil? :validate_constraint ...)
82
- PATTERN
83
-
84
- # @param corrector [RuboCop::Cop::Corrector]
85
- # @param node [RuboCop::AST::SendNode]
86
- # @return [void]
87
- def autocorrect(
88
- corrector,
89
- node
90
- )
91
- table_name, column_name = parse_table_name_and_column_name(node)
92
- corrector.replace(
93
- node,
94
- format(
95
- "add_check_constraint :%<table>s, '%<column>s IS NOT NULL', name: '%<constraint>s', validate: false",
96
- column: column_name,
97
- constraint: "#{table_name}_#{column_name}_is_not_null",
98
- table: table_name
99
- )
100
- )
101
- end
102
-
103
- # @param node [RuboCop::AST::SendNode]
104
- # @return [Boolean]
105
- def called_after_validate_constraint?(node)
106
- node.left_siblings.any? do |sibling|
107
- validate_constraint?(sibling)
108
- end
109
- end
110
-
111
- # @param node [RuboCop::AST::SendNode]
112
- # @return [Boolean]
113
- def called_before_remove_check_constraint?(node)
114
- node.right_siblings.any? do |sibling|
115
- remove_check_constraint?(sibling)
116
- end
117
- end
118
-
119
- # @param node [RuboCop::AST::SendNode]
120
- # @return [Boolean]
121
- def in_second_migration?(node)
122
- called_after_validate_constraint?(node) ||
123
- called_before_remove_check_constraint?(node)
124
- end
125
- end
126
- end
127
- end
128
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Sevencop
6
- # Create tables without `force: true` option.
7
- #
8
- # The `force: true` option can drop an existing table.
9
- # If you indend to drop an existing table, explicitly call `drop_table` first.
10
- #
11
- # @example
12
- # # bad
13
- # class CreateUsers < ActiveRecord::Migration[7.0]
14
- # def change
15
- # create_table :users, force: true
16
- # end
17
- # end
18
- #
19
- # # good
20
- # class CreateUsers < ActiveRecord::Migration[7.0]
21
- # def change
22
- # create_table :users
23
- # end
24
- # end
25
- class RailsMigrationCreateTableForce < RuboCop::Cop::Base
26
- extend AutoCorrector
27
-
28
- include RangeHelp
29
-
30
- MSG = 'Create tables without `force: true` option.'
31
-
32
- RESTRICT_ON_SEND = %i[
33
- create_table
34
- ].freeze
35
-
36
- # @param node [RuboCop::AST::SendNode]
37
- # @return [void]
38
- def on_send(node)
39
- option_node = option_force_true_from_create_table(node)
40
- return unless option_node
41
-
42
- add_offense(option_node) do |corrector|
43
- autocorrect(corrector, option_node)
44
- end
45
- end
46
-
47
- private
48
-
49
- # @!method option_force_true_from_create_table(node)
50
- # @param node [RuboCop::AST::SendNode]
51
- # @return [RuboCop::AST::PairNode, nil]
52
- def_node_matcher :option_force_true_from_create_table, <<~PATTERN
53
- (send
54
- nil?
55
- :create_table
56
- _
57
- (hash
58
- <
59
- $(pair
60
- (sym :force)
61
- true
62
- )
63
- ...
64
- >
65
- )
66
- )
67
- PATTERN
68
-
69
- # @param corrector [RuboCop::Cop::Corrector]
70
- # @param node [RuboCop::AST::PairNode]
71
- # @return [void]
72
- def autocorrect(
73
- corrector,
74
- node
75
- )
76
- corrector.remove(
77
- range_with_surrounding_comma(
78
- range_with_surrounding_space(
79
- node.location.expression,
80
- side: :left
81
- ),
82
- :left
83
- )
84
- )
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Sevencop
6
- # Prefer `jsonb` to `json`.
7
- #
8
- # In PostgreSQL, there is no equality operator for the json column type,
9
- # which can cause errors for existing `SELECT DISTINCT` queries in your application.
10
- #
11
- # @safety
12
- # Only meaningful in PostgreSQL.
13
- #
14
- # @example
15
- # # bad
16
- # add_column :users, :properties, :json
17
- #
18
- # # good
19
- # add_column :users, :properties, :jsonb
20
- class RailsMigrationJsonb < RuboCop::Cop::Base
21
- extend AutoCorrector
22
-
23
- MSG = 'Prefer `jsonb` to `json`.'
24
-
25
- RESTRICT_ON_SEND = %i[
26
- add_column
27
- change
28
- change_column
29
- json
30
- ].freeze
31
-
32
- # @param node [RuboCop::AST::SendNode]
33
- # @return [void]
34
- def on_send(node)
35
- json_range = json_range_from_target_send_node(node)
36
- return unless json_range
37
-
38
- add_offense(json_range) do |corrector|
39
- corrector.replace(json_range, 'jsonb')
40
- end
41
- end
42
-
43
- private
44
-
45
- # @!method json_type_node_from_add_column(node)
46
- # @param node [RuboCop::AST::SendNode]
47
- # @return [RuboCop::AST::SymNode, nil]
48
- def_node_matcher :json_type_node_from_add_column, <<~PATTERN
49
- (send
50
- nil?
51
- _
52
- _
53
- _
54
- $(sym :json)
55
- )
56
- PATTERN
57
- alias json_type_node_from_change_column json_type_node_from_add_column
58
-
59
- # @!method json_type_node_from_change(node)
60
- # @param node [RuboCop::AST::SendNode]
61
- # @return [RuboCop::AST::SymNode, nil]
62
- def_node_matcher :json_type_node_from_change, <<~PATTERN
63
- (send
64
- lvar
65
- _
66
- _
67
- $(sym :json)
68
- )
69
- PATTERN
70
-
71
- # @!method json_type_node_from_json(node)
72
- # @param node [RuboCop::AST::SendNode]
73
- # @return [RuboCop::AST::SendNode, nil]
74
- def_node_matcher :json_type_node_from_json, <<~PATTERN
75
- $(send
76
- lvar
77
- _
78
- ...
79
- )
80
- PATTERN
81
-
82
- # @param corrector [RuboCop::Cop::Corrector]
83
- # @param node [RuboCop::AST::SendNode, RuboCop::AST::SymNode]
84
- # @return [void]
85
- def autocorrect(
86
- corrector,
87
- node
88
- )
89
- corrector.replace(node, 'jsonb')
90
- end
91
-
92
- # @param node [RuboCop::AST::SendNode]
93
- # @return [RuboCop::AST::SymNode, nil]
94
- def json_node_from_target_send_node(node)
95
- case node.method_name
96
- when :add_column
97
- json_type_node_from_add_column(node)
98
- when :change
99
- json_type_node_from_change(node)
100
- when :change_column
101
- json_type_node_from_change_column(node)
102
- when :json
103
- json_type_node_from_json(node)
104
- end
105
- end
106
-
107
- # @param node [RuboCop::AST::SendNode, RuboCop::AST::SymNode]
108
- # @return [Parser::Source::Range]
109
- def json_range_from_json_node(node)
110
- case node.type
111
- when :send
112
- node.location.selector
113
- when :sym
114
- node.location.expression.with(
115
- begin_pos: node.location.expression.begin_pos + 1
116
- )
117
- end
118
- end
119
-
120
- # @param node [RuboCop::AST::SendNode]
121
- # @return [Parser::Source::Range]
122
- def json_range_from_target_send_node(node)
123
- json_node = json_node_from_target_send_node(node)
124
- return unless json_node
125
-
126
- json_range_from_json_node(json_node)
127
- end
128
- end
129
- end
130
- end
131
- end
@@ -1,258 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/inflector'
4
- require 'pathname'
5
-
6
- module RuboCop
7
- module Cop
8
- module Sevencop
9
- # Make sure the column is already ignored by the running app before removing it.
10
- #
11
- # Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
12
- #
13
- # @safety
14
- # The logic to check if it is included in `ignored_columns` may fail.
15
- #
16
- # @example
17
- # # bad
18
- # class User < ApplicationRecord
19
- # end
20
- #
21
- # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
22
- # def change
23
- # remove_column :users, :some_column
24
- # end
25
- # end
26
- #
27
- # # good
28
- # class User < ApplicationRecord
29
- # self.ignored_columns += %w[some_column]
30
- # end
31
- #
32
- # class RemoveUsersSomeColumn < ActiveRecord::Migration[7.0]
33
- # def change
34
- # remove_column :users, :some_column
35
- # end
36
- # end
37
- class RailsMigrationRemoveColumn < RuboCop::Cop::Base
38
- MSG = 'Make sure the column is already ignored by the running app before removing it.'
39
-
40
- RESTRICT_ON_SEND = %i[
41
- remove_column
42
- ].freeze
43
-
44
- # @param node [RuboCop::AST::SendNode]
45
- # @return [void]
46
- def on_send(node)
47
- return unless bad?(node)
48
-
49
- add_offense(node)
50
- end
51
-
52
- private
53
-
54
- # @!method ignored_columns?(node)
55
- # @param node [RuboCop::AST::Node]
56
- # @return [Boolean]
57
- def_node_matcher :ignored_columns?, <<~PATTERN
58
- (send
59
- self
60
- :ignored_columns
61
- ...
62
- )
63
- PATTERN
64
-
65
- # @!method remove_column?(node)
66
- # @param node [RuboCop::AST::SendNode]
67
- # @return [Boolean]
68
- def_node_matcher :remove_column?, <<~PATTERN
69
- (send
70
- nil?
71
- :remove_column
72
- ...
73
- )
74
- PATTERN
75
-
76
- # @!method ignored_column_nodes_from(node)
77
- # @param node [RuboCop::AST::Node]
78
- # @return [Array<String, Symbol>, nil]
79
- def_node_matcher :ignored_column_nodes_from, <<~PATTERN
80
- `{
81
- (op_asgn
82
- (send
83
- self
84
- :ignored_columns
85
- ...
86
- )
87
- :+
88
- (array
89
- ({str sym} $_)*
90
- )
91
- )
92
-
93
- (send
94
- self
95
- :ignored_columns=
96
- (array
97
- ({str sym} $_)*
98
- )
99
- )
100
- }
101
- PATTERN
102
-
103
- # @param node [RuboCop::AST::SendNode]
104
- # @return [Boolean]
105
- def bad?(node)
106
- remove_column?(node) &&
107
- !ignored?(node)
108
- end
109
-
110
- # @param node [RuboCop::AST::SendNode]
111
- # @return [String, nil]
112
- def find_column_name_from(node)
113
- node.arguments[1].value&.to_s
114
- end
115
-
116
- # @param table_name [String]
117
- # @return [Array<String>]
118
- def find_ignored_column_names_from(table_name)
119
- pathname = model_pathname_from(
120
- singularize(table_name)
121
- )
122
- return [] unless pathname.exist?
123
-
124
- ignored_column_nodes = ignored_column_nodes_from(
125
- parse(
126
- content: pathname.read,
127
- path: pathname.to_s
128
- )
129
- )
130
- return [] unless ignored_column_nodes
131
-
132
- ignored_column_nodes.map(&:to_s)
133
- end
134
-
135
- # @param node [RuboCop::AST::SendNode]
136
- # @return [String, nil]
137
- def find_table_name_from(node)
138
- node.arguments[0].value&.to_s
139
- end
140
-
141
- # @param node [RuboCop::AST::SendNode]
142
- # @return [Boolean]
143
- def ignored?(node)
144
- find_ignored_column_names_from(
145
- find_table_name_from(node)
146
- ).include?(
147
- find_column_name_from(node)
148
- )
149
- end
150
-
151
- # @param snake_cased_model_name [String]
152
- # @return [Pathname]
153
- def model_pathname_from(snake_cased_model_name)
154
- ::Pathname.new("app/models/#{snake_cased_model_name}.rb")
155
- end
156
-
157
- # @param content [String]
158
- # @param path [String]
159
- # @return [RuboCop::AST::Node]
160
- def parse(
161
- content:,
162
- path:
163
- )
164
- Parser.call(
165
- content: content,
166
- path: path,
167
- target_ruby_version: target_ruby_version
168
- )
169
- end
170
-
171
- # @return [#parse]
172
- def parser
173
- parser_class.new(::RuboCop::AST::Builder.new)
174
- end
175
-
176
- # @return [Class]
177
- def parser_class
178
- ::Parser.const_get(
179
- "Ruby#{target_ruby_version.to_s.delete('.')}"
180
- )
181
- end
182
-
183
- # @param plural [String]
184
- # @return [String]
185
- def singularize(plural)
186
- ::ActiveSupport::Inflector.singularize(plural)
187
- end
188
-
189
- class Parser
190
- class << self
191
- # @param content [String]
192
- # @param path [String]
193
- # @param target_ruby_version [#to_s]
194
- # @return [RuboCop::AST::Node]
195
- def call(
196
- content:,
197
- path:,
198
- target_ruby_version:
199
- )
200
- new(
201
- content: content,
202
- path: path,
203
- target_ruby_version: target_ruby_version
204
- ).call
205
- end
206
- end
207
-
208
- # @param content [String]
209
- # @param path [String]
210
- # @param target_ruby_version [#to_s]
211
- def initialize(
212
- content:,
213
- path:,
214
- target_ruby_version:
215
- )
216
- @content = content
217
- @path = path
218
- @target_ruby_version = target_ruby_version
219
- end
220
-
221
- # @return [RuboCop::AST::Node]
222
- def call
223
- parser.parse(buffer)
224
- end
225
-
226
- private
227
-
228
- # @return [Parser::Source::Buffer]
229
- def buffer
230
- ::Parser::Source::Buffer.new(
231
- @path,
232
- source: @content
233
- )
234
- end
235
-
236
- # @return [RuboCop::AST::Builder]
237
- def builder
238
- ::RuboCop::AST::Builder.new
239
- end
240
-
241
- def parser
242
- parser_class.new(builder)
243
- end
244
-
245
- # @return [Class]
246
- def parser_class
247
- ::Parser.const_get(parser_class_name)
248
- end
249
-
250
- # @return [String]
251
- def parser_class_name
252
- "Ruby#{@target_ruby_version.to_s.delete('.')}"
253
- end
254
- end
255
- end
256
- end
257
- end
258
- end