sevencop 0.22.0 → 0.23.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.
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