rubocop-rails 2.4.1

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +92 -0
  4. data/bin/setup +7 -0
  5. data/config/default.yml +510 -0
  6. data/lib/rubocop-rails.rb +12 -0
  7. data/lib/rubocop/cop/mixin/target_rails_version.rb +16 -0
  8. data/lib/rubocop/cop/rails/action_filter.rb +111 -0
  9. data/lib/rubocop/cop/rails/active_record_aliases.rb +48 -0
  10. data/lib/rubocop/cop/rails/active_record_override.rb +82 -0
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +69 -0
  12. data/lib/rubocop/cop/rails/application_controller.rb +36 -0
  13. data/lib/rubocop/cop/rails/application_job.rb +40 -0
  14. data/lib/rubocop/cop/rails/application_mailer.rb +40 -0
  15. data/lib/rubocop/cop/rails/application_record.rb +40 -0
  16. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  17. data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
  18. data/lib/rubocop/cop/rails/blank.rb +164 -0
  19. data/lib/rubocop/cop/rails/bulk_change_table.rb +293 -0
  20. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
  21. data/lib/rubocop/cop/rails/date.rb +161 -0
  22. data/lib/rubocop/cop/rails/delegate.rb +132 -0
  23. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
  24. data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
  25. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  26. data/lib/rubocop/cop/rails/enum_uniqueness.rb +65 -0
  27. data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
  28. data/lib/rubocop/cop/rails/exit.rb +67 -0
  29. data/lib/rubocop/cop/rails/file_path.rb +108 -0
  30. data/lib/rubocop/cop/rails/find_by.rb +55 -0
  31. data/lib/rubocop/cop/rails/find_each.rb +51 -0
  32. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
  33. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
  34. data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
  35. data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
  36. data/lib/rubocop/cop/rails/http_status.rb +160 -0
  37. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
  38. data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
  39. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
  40. data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
  41. data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
  42. data/lib/rubocop/cop/rails/output.rb +49 -0
  43. data/lib/rubocop/cop/rails/output_safety.rb +99 -0
  44. data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
  45. data/lib/rubocop/cop/rails/presence.rb +148 -0
  46. data/lib/rubocop/cop/rails/present.rb +153 -0
  47. data/lib/rubocop/cop/rails/rake_environment.rb +91 -0
  48. data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
  49. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
  50. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
  51. data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
  52. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  53. data/lib/rubocop/cop/rails/relative_date_constant.rb +102 -0
  54. data/lib/rubocop/cop/rails/request_referer.rb +56 -0
  55. data/lib/rubocop/cop/rails/reversible_migration.rb +284 -0
  56. data/lib/rubocop/cop/rails/safe_navigation.rb +85 -0
  57. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +48 -0
  58. data/lib/rubocop/cop/rails/save_bang.rb +331 -0
  59. data/lib/rubocop/cop/rails/scope_args.rb +29 -0
  60. data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
  61. data/lib/rubocop/cop/rails/time_zone.rb +249 -0
  62. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
  63. data/lib/rubocop/cop/rails/unknown_env.rb +84 -0
  64. data/lib/rubocop/cop/rails/validation.rb +147 -0
  65. data/lib/rubocop/cop/rails_cops.rb +61 -0
  66. data/lib/rubocop/rails.rb +12 -0
  67. data/lib/rubocop/rails/inject.rb +18 -0
  68. data/lib/rubocop/rails/version.rb +10 -0
  69. metadata +148 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that models subclass ApplicationRecord with Rails 5.0.
7
+ #
8
+ # @example
9
+ #
10
+ # # good
11
+ # class Rails5Model < ApplicationRecord
12
+ # # ...
13
+ # end
14
+ #
15
+ # # bad
16
+ # class Rails4Model < ActiveRecord::Base
17
+ # # ...
18
+ # end
19
+ class ApplicationRecord < Cop
20
+ extend TargetRailsVersion
21
+
22
+ minimum_target_rails_version 5.0
23
+
24
+ MSG = 'Models should subclass `ApplicationRecord`.'
25
+ SUPERCLASS = 'ApplicationRecord'
26
+ BASE_PATTERN = '(const (const nil? :ActiveRecord) :Base)'
27
+
28
+ # rubocop:disable Layout/ClassStructure
29
+ include RuboCop::Cop::EnforceSuperclass
30
+ # rubocop:enable Layout/ClassStructure
31
+
32
+ def autocorrect(node)
33
+ lambda do |corrector|
34
+ corrector.replace(node.source_range, self.class::SUPERCLASS)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ #
7
+ # Use `assert_not` instead of `assert !`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert !x
12
+ #
13
+ # # good
14
+ # assert_not x
15
+ #
16
+ class AssertNot < RuboCop::Cop::Cop
17
+ MSG = 'Prefer `assert_not` over `assert !`.'
18
+
19
+ def_node_matcher :offensive?, '(send nil? :assert (send ... :!) ...)'
20
+
21
+ def on_send(node)
22
+ add_offense(node) if offensive?(node)
23
+ end
24
+
25
+ def autocorrect(node)
26
+ expression = node.loc.expression
27
+
28
+ lambda do |corrector|
29
+ corrector.replace(
30
+ expression,
31
+ corrected_source(expression.source)
32
+ )
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def corrected_source(source)
39
+ source.gsub(/^assert(\(| ) *! */, 'assert_not\\1')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for belongs_to associations where we control whether the
7
+ # association is required via the deprecated `required` option instead.
8
+ #
9
+ # Since Rails 5, belongs_to associations are required by default and this
10
+ # can be controlled through the use of `optional: true`.
11
+ #
12
+ # From the release notes:
13
+ #
14
+ # belongs_to will now trigger a validation error by default if the
15
+ # association is not present. You can turn this off on a
16
+ # per-association basis with optional: true. Also deprecate required
17
+ # option in favor of optional for belongs_to. (Pull Request)
18
+ #
19
+ # In the case that the developer is doing `required: false`, we
20
+ # definitely want to autocorrect to `optional: true`.
21
+ #
22
+ # However, without knowing whether they've set overridden the default
23
+ # value of `config.active_record.belongs_to_required_by_default`, we
24
+ # can't say whether it's safe to remove `required: true` or whether we
25
+ # should replace it with `optional: false` (or, similarly, remove a
26
+ # superfluous `optional: false`). Therefore, in the cases we're using
27
+ # `required: true`, we'll simply invert it to `optional: false` and the
28
+ # user can remove depending on their defaults.
29
+ #
30
+ # @example
31
+ # # bad
32
+ # class Post < ApplicationRecord
33
+ # belongs_to :blog, required: false
34
+ # end
35
+ #
36
+ # # good
37
+ # class Post < ApplicationRecord
38
+ # belongs_to :blog, optional: true
39
+ # end
40
+ #
41
+ # # bad
42
+ # class Post < ApplicationRecord
43
+ # belongs_to :blog, required: true
44
+ # end
45
+ #
46
+ # # good
47
+ # class Post < ApplicationRecord
48
+ # belongs_to :blog, optional: false
49
+ # end
50
+ #
51
+ # @see https://guides.rubyonrails.org/5_0_release_notes.html
52
+ # @see https://github.com/rails/rails/pull/18937
53
+ class BelongsTo < Cop
54
+ extend TargetRailsVersion
55
+
56
+ minimum_target_rails_version 5.0
57
+
58
+ SUPERFLOUS_REQUIRE_FALSE_MSG =
59
+ 'You specified `required: false`, in Rails > 5.0 the required ' \
60
+ 'option is deprecated and you want to use `optional: true`.'
61
+
62
+ SUPERFLOUS_REQUIRE_TRUE_MSG =
63
+ 'You specified `required: true`, in Rails > 5.0 the required ' \
64
+ 'option is deprecated and you want to use `optional: false`. ' \
65
+ 'In most configurations, this is the default and you can omit ' \
66
+ 'this option altogether'
67
+
68
+ def_node_matcher :match_belongs_to_with_options, <<~PATTERN
69
+ (send _ :belongs_to _
70
+ (hash <$(pair (sym :required) ${true false}) ...>)
71
+ )
72
+ PATTERN
73
+
74
+ def on_send(node)
75
+ match_belongs_to_with_options(node) do |_option_node, option_value|
76
+ message =
77
+ if option_value.true_type?
78
+ SUPERFLOUS_REQUIRE_TRUE_MSG
79
+ elsif option_value.false_type?
80
+ SUPERFLOUS_REQUIRE_FALSE_MSG
81
+ end
82
+
83
+ add_offense(node, message: message, location: :selector)
84
+ end
85
+ end
86
+
87
+ def autocorrect(node)
88
+ option_node, option_value = match_belongs_to_with_options(node)
89
+ return unless option_node
90
+
91
+ lambda do |corrector|
92
+ if option_value.true_type?
93
+ corrector.replace(option_node.loc.expression, 'optional: false')
94
+ elsif option_value.false_type?
95
+ corrector.replace(option_node.loc.expression, 'optional: true')
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for code that can be written with simpler conditionals
7
+ # using `Object#blank?` defined by Active Support.
8
+ #
9
+ # Interaction with `Style/UnlessElse`:
10
+ # The configuration of `NotPresent` will not produce an offense in the
11
+ # context of `unless else` if `Style/UnlessElse` is inabled. This is
12
+ # to prevent interference between the auto-correction of the two cops.
13
+ #
14
+ # @example NilOrEmpty: true (default)
15
+ # # Converts usages of `nil? || empty?` to `blank?`
16
+ #
17
+ # # bad
18
+ # foo.nil? || foo.empty?
19
+ # foo == nil || foo.empty?
20
+ #
21
+ # # good
22
+ # foo.blank?
23
+ #
24
+ # @example NotPresent: true (default)
25
+ # # Converts usages of `!present?` to `blank?`
26
+ #
27
+ # # bad
28
+ # !foo.present?
29
+ #
30
+ # # good
31
+ # foo.blank?
32
+ #
33
+ # @example UnlessPresent: true (default)
34
+ # # Converts usages of `unless present?` to `if blank?`
35
+ #
36
+ # # bad
37
+ # something unless foo.present?
38
+ #
39
+ # # good
40
+ # something if foo.blank?
41
+ #
42
+ # # bad
43
+ # unless foo.present?
44
+ # something
45
+ # end
46
+ #
47
+ # # good
48
+ # if foo.blank?
49
+ # something
50
+ # end
51
+ #
52
+ # # good
53
+ # def blank?
54
+ # !present?
55
+ # end
56
+ class Blank < Cop
57
+ MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
58
+ MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
59
+ MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
60
+ '`%<current>s`.'
61
+
62
+ # `(send nil $_)` is not actually a valid match for an offense. Nodes
63
+ # that have a single method call on the left hand side
64
+ # (`bar || foo.empty?`) will blow up when checking
65
+ # `(send (:nil) :== $_)`.
66
+ def_node_matcher :nil_or_empty?, <<~PATTERN
67
+ (or
68
+ {
69
+ (send $_ :!)
70
+ (send $_ :nil?)
71
+ (send $_ :== nil)
72
+ (send nil :== $_)
73
+ }
74
+ {
75
+ (send $_ :empty?)
76
+ (send (send (send $_ :empty?) :!) :!)
77
+ }
78
+ )
79
+ PATTERN
80
+
81
+ def_node_matcher :not_present?, '(send (send $_ :present?) :!)'
82
+
83
+ def_node_matcher :defining_blank?, '(def :blank? (args) ...)'
84
+
85
+ def_node_matcher :unless_present?, <<~PATTERN
86
+ (:if $(send $_ :present?) {nil? (...)} ...)
87
+ PATTERN
88
+
89
+ def on_send(node)
90
+ return unless cop_config['NotPresent']
91
+
92
+ not_present?(node) do |receiver|
93
+ # accepts !present? if its in the body of a `blank?` method
94
+ next if defining_blank?(node.parent)
95
+
96
+ add_offense(node,
97
+ message: format(MSG_NOT_PRESENT,
98
+ prefer: replacement(receiver),
99
+ current: node.source))
100
+ end
101
+ end
102
+
103
+ def on_or(node)
104
+ return unless cop_config['NilOrEmpty']
105
+
106
+ nil_or_empty?(node) do |var1, var2|
107
+ return unless var1 == var2
108
+
109
+ add_offense(node,
110
+ message: format(MSG_NIL_OR_EMPTY,
111
+ prefer: replacement(var1),
112
+ current: node.source))
113
+ end
114
+ end
115
+
116
+ def on_if(node)
117
+ return unless cop_config['UnlessPresent']
118
+ return unless node.unless?
119
+ return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
120
+
121
+ unless_present?(node) do |method_call, receiver|
122
+ range = unless_condition(node, method_call)
123
+
124
+ add_offense(node,
125
+ location: range,
126
+ message: format(MSG_UNLESS_PRESENT,
127
+ prefer: replacement(receiver),
128
+ current: range.source))
129
+ end
130
+ end
131
+
132
+ def autocorrect(node)
133
+ lambda do |corrector|
134
+ method_call, variable1 = unless_present?(node)
135
+
136
+ if method_call
137
+ corrector.replace(node.loc.keyword, 'if')
138
+ range = method_call.loc.expression
139
+ else
140
+ variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
141
+ range = node.loc.expression
142
+ end
143
+
144
+ corrector.replace(range, replacement(variable1))
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ def unless_condition(node, method_call)
151
+ if node.modifier_form?
152
+ node.loc.keyword.join(node.loc.expression.end)
153
+ else
154
+ node.loc.expression.begin.join(method_call.loc.expression)
155
+ end
156
+ end
157
+
158
+ def replacement(node)
159
+ node.respond_to?(:source) ? "#{node.source}.blank?" : 'blank?'
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This Cop checks whether alter queries are combinable.
7
+ # If combinable queries are detected, it suggests to you
8
+ # to use `change_table` with `bulk: true` instead.
9
+ # This option causes the migration to generate a single
10
+ # ALTER TABLE statement combining multiple column alterations.
11
+ #
12
+ # The `bulk` option is only supported on the MySQL and
13
+ # the PostgreSQL (5.2 later) adapter; thus it will
14
+ # automatically detect an adapter from `development` environment
15
+ # in `config/database.yml` when the `Database` option is not set.
16
+ # If the adapter is not `mysql2` or `postgresql`,
17
+ # this Cop ignores offenses.
18
+ #
19
+ # @example
20
+ # # bad
21
+ # def change
22
+ # add_column :users, :name, :string, null: false
23
+ # add_column :users, :nickname, :string
24
+ #
25
+ # # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL
26
+ # # ALTER TABLE `users` ADD `nickname` varchar(255)
27
+ # end
28
+ #
29
+ # # good
30
+ # def change
31
+ # change_table :users, bulk: true do |t|
32
+ # t.string :name, null: false
33
+ # t.string :nickname
34
+ # end
35
+ #
36
+ # # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL,
37
+ # # ADD `nickname` varchar(255)
38
+ # end
39
+ #
40
+ # @example
41
+ # # bad
42
+ # def change
43
+ # change_table :users do |t|
44
+ # t.string :name, null: false
45
+ # t.string :nickname
46
+ # end
47
+ # end
48
+ #
49
+ # # good
50
+ # def change
51
+ # change_table :users, bulk: true do |t|
52
+ # t.string :name, null: false
53
+ # t.string :nickname
54
+ # end
55
+ # end
56
+ #
57
+ # # good
58
+ # # When you don't want to combine alter queries.
59
+ # def change
60
+ # change_table :users, bulk: false do |t|
61
+ # t.string :name, null: false
62
+ # t.string :nickname
63
+ # end
64
+ # end
65
+ #
66
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
67
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
68
+ class BulkChangeTable < Cop
69
+ MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
70
+ You can combine alter queries using `bulk: true` options.
71
+ MSG
72
+ MSG_FOR_ALTER_METHODS = <<~MSG.chomp
73
+ You can use `change_table :%<table>s, bulk: true` to combine alter queries.
74
+ MSG
75
+
76
+ MYSQL = 'mysql'
77
+ POSTGRESQL = 'postgresql'
78
+
79
+ MIGRATION_METHODS = %i[change up down].freeze
80
+
81
+ COMBINABLE_TRANSFORMATIONS = %i[
82
+ primary_key
83
+ column
84
+ string
85
+ text
86
+ integer
87
+ bigint
88
+ float
89
+ decimal
90
+ numeric
91
+ datetime
92
+ timestamp
93
+ time
94
+ date
95
+ binary
96
+ boolean
97
+ json
98
+ virtual
99
+ remove
100
+ change
101
+ timestamps
102
+ remove_timestamps
103
+ ].freeze
104
+
105
+ COMBINABLE_ALTER_METHODS = %i[
106
+ add_column
107
+ remove_column
108
+ remove_columns
109
+ change_column
110
+ add_timestamps
111
+ remove_timestamps
112
+ ].freeze
113
+
114
+ MYSQL_COMBINABLE_TRANSFORMATIONS = %i[
115
+ rename
116
+ index
117
+ remove_index
118
+ ].freeze
119
+
120
+ MYSQL_COMBINABLE_ALTER_METHODS = %i[
121
+ rename_column
122
+ add_index
123
+ remove_index
124
+ ].freeze
125
+
126
+ POSTGRESQL_COMBINABLE_TRANSFORMATIONS = %i[
127
+ change_default
128
+ ].freeze
129
+
130
+ POSTGRESQL_COMBINABLE_ALTER_METHODS = %i[
131
+ change_column_default
132
+ ].freeze
133
+
134
+ def on_def(node)
135
+ return unless support_bulk_alter?
136
+ return unless MIGRATION_METHODS.include?(node.method_name)
137
+ return unless node.body
138
+
139
+ recorder = AlterMethodsRecorder.new
140
+
141
+ node.body.child_nodes.each do |child_node|
142
+ if call_to_combinable_alter_method? child_node
143
+ recorder.process(child_node)
144
+ else
145
+ recorder.flush
146
+ end
147
+ end
148
+
149
+ recorder.offensive_nodes.each { |n| add_offense_for_alter_methods(n) }
150
+ end
151
+
152
+ def on_send(node)
153
+ return unless support_bulk_alter?
154
+ return unless node.command?(:change_table)
155
+ return if include_bulk_options?(node)
156
+ return unless node.block_node
157
+
158
+ send_nodes = node.block_node.body.each_child_node(:send).to_a
159
+
160
+ transformations = send_nodes.select do |send_node|
161
+ combinable_transformations.include?(send_node.method_name)
162
+ end
163
+
164
+ add_offense_for_change_table(node) if transformations.size > 1
165
+ end
166
+
167
+ private
168
+
169
+ # @param node [RuboCop::AST::SendNode] (send nil? :change_table ...)
170
+ def include_bulk_options?(node)
171
+ # arguments: [{(sym :table)(str "table")} (hash (pair (sym :bulk) _))]
172
+ options = node.arguments[1]
173
+ return false unless options
174
+
175
+ options.hash_type? &&
176
+ options.keys.any? { |key| key.sym_type? && key.value == :bulk }
177
+ end
178
+
179
+ def database
180
+ cop_config['Database'] || database_from_yaml
181
+ end
182
+
183
+ def database_from_yaml
184
+ return nil unless database_yaml
185
+
186
+ case database_yaml['adapter']
187
+ when 'mysql2'
188
+ MYSQL
189
+ when 'postgresql'
190
+ POSTGRESQL
191
+ end
192
+ end
193
+
194
+ def database_yaml
195
+ return nil unless File.exist?('config/database.yml')
196
+
197
+ yaml = YAML.load_file('config/database.yml')
198
+ return nil unless yaml.is_a? Hash
199
+
200
+ config = yaml['development']
201
+ return nil unless config.is_a?(Hash)
202
+
203
+ config
204
+ rescue Psych::SyntaxError
205
+ nil
206
+ end
207
+
208
+ def support_bulk_alter?
209
+ case database
210
+ when MYSQL
211
+ true
212
+ when POSTGRESQL
213
+ # Add bulk alter support for PostgreSQL in 5.2.0
214
+ # @see https://github.com/rails/rails/pull/31331
215
+ target_rails_version >= 5.2
216
+ else
217
+ false
218
+ end
219
+ end
220
+
221
+ def call_to_combinable_alter_method?(child_node)
222
+ child_node.send_type? &&
223
+ combinable_alter_methods.include?(child_node.method_name)
224
+ end
225
+
226
+ def combinable_alter_methods
227
+ case database
228
+ when MYSQL
229
+ COMBINABLE_ALTER_METHODS + MYSQL_COMBINABLE_ALTER_METHODS
230
+ when POSTGRESQL
231
+ COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
232
+ end
233
+ end
234
+
235
+ def combinable_transformations
236
+ case database
237
+ when MYSQL
238
+ COMBINABLE_TRANSFORMATIONS + MYSQL_COMBINABLE_TRANSFORMATIONS
239
+ when POSTGRESQL
240
+ COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
241
+ end
242
+ end
243
+
244
+ # @param node [RuboCop::AST::SendNode]
245
+ def add_offense_for_alter_methods(node)
246
+ # arguments: [{(sym :table)(str "table")} ...]
247
+ table_node = node.arguments[0]
248
+ return unless table_node.is_a? RuboCop::AST::BasicLiteralNode
249
+
250
+ message = format(MSG_FOR_ALTER_METHODS, table: table_node.value)
251
+ add_offense(node, message: message)
252
+ end
253
+
254
+ # @param node [RuboCop::AST::SendNode]
255
+ def add_offense_for_change_table(node)
256
+ add_offense(node, message: MSG_FOR_CHANGE_TABLE)
257
+ end
258
+
259
+ # Record combinable alter methods and register offensive nodes.
260
+ class AlterMethodsRecorder
261
+ def initialize
262
+ @nodes = []
263
+ @offensive_nodes = []
264
+ end
265
+
266
+ # @param new_node [RuboCop::AST::SendNode]
267
+ def process(new_node)
268
+ # arguments: [{(sym :table)(str "table")} ...]
269
+ table_node = new_node.arguments[0]
270
+ if table_node.is_a? RuboCop::AST::BasicLiteralNode
271
+ flush unless @nodes.all? do |node|
272
+ node.arguments[0].value.to_s == table_node.value.to_s
273
+ end
274
+ @nodes << new_node
275
+ else
276
+ flush
277
+ end
278
+ end
279
+
280
+ def flush
281
+ @offensive_nodes << @nodes.first if @nodes.size > 1
282
+ @nodes = []
283
+ end
284
+
285
+ def offensive_nodes
286
+ flush
287
+ @offensive_nodes
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end