rubocop-rails 2.15.2 → 2.20.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +23 -2
  4. data/config/default.yml +181 -13
  5. data/config/obsoletion.yml +10 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +3 -6
  7. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +1 -3
  8. data/lib/rubocop/cop/mixin/enforce_superclass.rb +1 -1
  9. data/lib/rubocop/cop/mixin/index_method.rb +7 -17
  10. data/lib/rubocop/cop/mixin/migrations_helper.rb +1 -1
  11. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
  12. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  13. data/lib/rubocop/cop/rails/action_filter.rb +1 -1
  14. data/lib/rubocop/cop/rails/action_order.rb +116 -0
  15. data/lib/rubocop/cop/rails/active_record_aliases.rb +3 -4
  16. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +6 -3
  17. data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
  18. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  19. data/lib/rubocop/cop/rails/add_column_index.rb +2 -5
  20. data/lib/rubocop/cop/rails/application_controller.rb +1 -1
  21. data/lib/rubocop/cop/rails/application_job.rb +2 -2
  22. data/lib/rubocop/cop/rails/application_mailer.rb +1 -1
  23. data/lib/rubocop/cop/rails/application_record.rb +1 -1
  24. data/lib/rubocop/cop/rails/arel_star.rb +1 -1
  25. data/lib/rubocop/cop/rails/assert_not.rb +1 -2
  26. data/lib/rubocop/cop/rails/belongs_to.rb +1 -4
  27. data/lib/rubocop/cop/rails/blank.rb +6 -7
  28. data/lib/rubocop/cop/rails/bulk_change_table.rb +7 -24
  29. data/lib/rubocop/cop/rails/compact_blank.rb +5 -1
  30. data/lib/rubocop/cop/rails/content_tag.rb +5 -6
  31. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +16 -3
  32. data/lib/rubocop/cop/rails/date.rb +15 -11
  33. data/lib/rubocop/cop/rails/delegate.rb +19 -8
  34. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +1 -1
  35. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +18 -14
  36. data/lib/rubocop/cop/rails/dot_separated_keys.rb +2 -2
  37. data/lib/rubocop/cop/rails/duration_arithmetic.rb +3 -3
  38. data/lib/rubocop/cop/rails/dynamic_find_by.rb +25 -13
  39. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +5 -1
  40. data/lib/rubocop/cop/rails/enum_hash.rb +1 -1
  41. data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -5
  42. data/lib/rubocop/cop/rails/environment_comparison.rb +2 -3
  43. data/lib/rubocop/cop/rails/file_path.rb +154 -27
  44. data/lib/rubocop/cop/rails/find_by_id.rb +2 -2
  45. data/lib/rubocop/cop/rails/find_each.rb +15 -5
  46. data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
  47. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +4 -6
  48. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  49. data/lib/rubocop/cop/rails/http_positional_arguments.rb +22 -11
  50. data/lib/rubocop/cop/rails/http_status.rb +6 -11
  51. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +2 -0
  52. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +7 -3
  53. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  54. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +23 -12
  55. data/lib/rubocop/cop/rails/index_by.rb +1 -1
  56. data/lib/rubocop/cop/rails/index_with.rb +1 -1
  57. data/lib/rubocop/cop/rails/inverse_of.rb +3 -9
  58. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +21 -15
  59. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -4
  60. data/lib/rubocop/cop/rails/mailer_name.rb +4 -4
  61. data/lib/rubocop/cop/rails/migration_class_name.rb +1 -1
  62. data/lib/rubocop/cop/rails/negate_include.rb +1 -1
  63. data/lib/rubocop/cop/rails/not_null_column.rb +9 -6
  64. data/lib/rubocop/cop/rails/output.rb +6 -8
  65. data/lib/rubocop/cop/rails/output_safety.rb +5 -1
  66. data/lib/rubocop/cop/rails/pluck.rb +44 -12
  67. data/lib/rubocop/cop/rails/pluck_id.rb +1 -1
  68. data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -2
  69. data/lib/rubocop/cop/rails/presence.rb +21 -12
  70. data/lib/rubocop/cop/rails/present.rb +8 -11
  71. data/lib/rubocop/cop/rails/rake_environment.rb +2 -2
  72. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  73. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +5 -7
  74. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +2 -2
  75. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
  76. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +30 -26
  77. data/lib/rubocop/cop/rails/reflection_class_name.rb +34 -1
  78. data/lib/rubocop/cop/rails/refute_methods.rb +1 -6
  79. data/lib/rubocop/cop/rails/relative_date_constant.rb +4 -7
  80. data/lib/rubocop/cop/rails/request_referer.rb +1 -2
  81. data/lib/rubocop/cop/rails/require_dependency.rb +1 -1
  82. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  83. data/lib/rubocop/cop/rails/reversible_migration.rb +14 -62
  84. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +1 -2
  85. data/lib/rubocop/cop/rails/root_join_chain.rb +1 -1
  86. data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
  87. data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
  88. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +1 -3
  89. data/lib/rubocop/cop/rails/save_bang.rb +10 -22
  90. data/lib/rubocop/cop/rails/short_i18n.rb +2 -5
  91. data/lib/rubocop/cop/rails/skips_model_validations.rb +2 -3
  92. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +9 -7
  93. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +71 -0
  94. data/lib/rubocop/cop/rails/time_zone.rb +27 -25
  95. data/lib/rubocop/cop/rails/time_zone_assignment.rb +1 -1
  96. data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
  97. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  98. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +8 -3
  99. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
  100. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +22 -19
  101. data/lib/rubocop/cop/rails/unknown_env.rb +2 -4
  102. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -1
  103. data/lib/rubocop/cop/rails/validation.rb +4 -12
  104. data/lib/rubocop/cop/rails/where_equals.rb +1 -1
  105. data/lib/rubocop/cop/rails/where_exists.rb +1 -1
  106. data/lib/rubocop/cop/rails/where_missing.rb +118 -0
  107. data/lib/rubocop/cop/rails/where_not.rb +1 -1
  108. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  109. data/lib/rubocop/cop/rails_cops.rb +12 -0
  110. data/lib/rubocop/rails/schema_loader/schema.rb +4 -4
  111. data/lib/rubocop/rails/version.rb +1 -1
  112. data/lib/rubocop/rails.rb +1 -1
  113. data/lib/rubocop-rails.rb +11 -0
  114. metadata +19 -9
  115. data/bin/console +0 -11
  116. data/bin/setup +0 -7
@@ -3,58 +3,65 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Identifies usages of file path joining process
7
- # to use `Rails.root.join` clause. It is used to add uniformity when
8
- # joining paths.
6
+ # Identifies usages of file path joining process to use `Rails.root.join` clause.
7
+ # It is used to add uniformity when joining paths.
9
8
  #
10
9
  # @example EnforcedStyle: slashes (default)
11
10
  # # bad
12
11
  # Rails.root.join('app', 'models', 'goober')
12
+ #
13
+ # # good
14
+ # Rails.root.join('app/models/goober')
15
+ #
16
+ # # bad
13
17
  # File.join(Rails.root, 'app/models/goober')
14
18
  # "#{Rails.root}/app/models/goober"
15
19
  #
16
20
  # # good
17
- # Rails.root.join('app/models/goober')
21
+ # Rails.root.join('app/models/goober').to_s
18
22
  #
19
23
  # @example EnforcedStyle: arguments
20
24
  # # bad
21
25
  # Rails.root.join('app/models/goober')
26
+ #
27
+ # # good
28
+ # Rails.root.join('app', 'models', 'goober')
29
+ #
30
+ # # bad
22
31
  # File.join(Rails.root, 'app/models/goober')
23
32
  # "#{Rails.root}/app/models/goober"
24
33
  #
25
34
  # # good
26
- # Rails.root.join('app', 'models', 'goober')
35
+ # Rails.root.join('app', 'models', 'goober').to_s
27
36
  #
28
37
  class FilePath < Base
38
+ extend AutoCorrector
39
+
29
40
  include ConfigurableEnforcedStyle
30
41
  include RangeHelp
31
42
 
32
- MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')`.'
33
- MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')`.'
43
+ MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')%<to_s>s`.'
44
+ MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')%<to_s>s`.'
34
45
  RESTRICT_ON_SEND = %i[join].freeze
35
46
 
36
47
  def_node_matcher :file_join_nodes?, <<~PATTERN
37
- (send (const nil? :File) :join ...)
48
+ (send (const {nil? cbase} :File) :join ...)
38
49
  PATTERN
39
50
 
40
51
  def_node_search :rails_root_nodes?, <<~PATTERN
41
- (send (const nil? :Rails) :root)
52
+ (send (const {nil? cbase} :Rails) :root)
42
53
  PATTERN
43
54
 
44
55
  def_node_matcher :rails_root_join_nodes?, <<~PATTERN
45
- (send (send (const nil? :Rails) :root) :join ...)
56
+ (send #rails_root_nodes? :join ...)
46
57
  PATTERN
47
58
 
48
59
  def on_dstr(node)
49
60
  return unless rails_root_nodes?(node)
50
- return unless node.children.last.str_type?
61
+ return if dstr_separated_by_colon?(node)
51
62
 
52
- last_child_source = node.children.last.source
53
- return unless last_child_source.start_with?('.') ||
54
- last_child_source.include?(File::SEPARATOR)
55
- return if last_child_source.start_with?(':')
56
-
57
- register_offense(node)
63
+ check_for_slash_after_rails_root_in_dstr(node)
64
+ check_for_extension_after_rails_root_join_in_dstr(node)
58
65
  end
59
66
 
60
67
  def on_send(node)
@@ -65,11 +72,33 @@ module RuboCop
65
72
 
66
73
  private
67
74
 
75
+ def check_for_slash_after_rails_root_in_dstr(node)
76
+ rails_root_index = find_rails_root_index(node)
77
+ slash_node = node.children[rails_root_index + 1]
78
+ return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
79
+
80
+ register_offense(node, require_to_s: false) do |corrector|
81
+ autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
82
+ end
83
+ end
84
+
85
+ def check_for_extension_after_rails_root_join_in_dstr(node)
86
+ rails_root_index = find_rails_root_index(node)
87
+ extension_node = node.children[rails_root_index + 1]
88
+ return unless extension_node?(extension_node)
89
+
90
+ register_offense(node, require_to_s: false) do |corrector|
91
+ autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
92
+ end
93
+ end
94
+
68
95
  def check_for_file_join_with_rails_root(node)
69
96
  return unless file_join_nodes?(node)
70
97
  return unless node.arguments.any? { |e| rails_root_nodes?(e) }
71
98
 
72
- register_offense(node)
99
+ register_offense(node, require_to_s: true) do |corrector|
100
+ autocorrect_file_join(corrector, node)
101
+ end
73
102
  end
74
103
 
75
104
  def check_for_rails_root_join_with_string_arguments(node)
@@ -79,7 +108,9 @@ module RuboCop
79
108
  return unless node.arguments.size > 1
80
109
  return unless node.arguments.all?(&:str_type?)
81
110
 
82
- register_offense(node)
111
+ register_offense(node, require_to_s: false) do |corrector|
112
+ autocorrect_rails_root_join_with_string_arguments(corrector, node)
113
+ end
83
114
  end
84
115
 
85
116
  def check_for_rails_root_join_with_slash_separated_path(node)
@@ -88,22 +119,118 @@ module RuboCop
88
119
  return unless rails_root_join_nodes?(node)
89
120
  return unless node.arguments.any? { |arg| string_with_slash?(arg) }
90
121
 
91
- register_offense(node)
122
+ register_offense(node, require_to_s: false) do |corrector|
123
+ autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
124
+ end
92
125
  end
93
126
 
94
127
  def string_with_slash?(node)
95
- node.str_type? && node.source.include?('/')
128
+ node.str_type? && node.source.include?(File::SEPARATOR)
96
129
  end
97
130
 
98
- def register_offense(node)
131
+ def register_offense(node, require_to_s:, &block)
99
132
  line_range = node.loc.column...node.loc.last_column
100
- source_range = source_range(processed_source.buffer, node.first_line,
101
- line_range)
102
- add_offense(source_range)
133
+ source_range = source_range(processed_source.buffer, node.first_line, line_range)
134
+
135
+ message = build_message(require_to_s)
136
+
137
+ add_offense(source_range, message: message, &block)
138
+ end
139
+
140
+ def build_message(require_to_s)
141
+ message_template = style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES
142
+ to_s = require_to_s ? '.to_s' : ''
143
+
144
+ format(message_template, to_s: to_s)
145
+ end
146
+
147
+ def dstr_separated_by_colon?(node)
148
+ node.children[1..].any? do |child|
149
+ child.str_type? && child.source.start_with?(':')
150
+ end
151
+ end
152
+
153
+ def autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
154
+ rails_root_node = node.children[rails_root_index].children.first
155
+ argument_source = extract_rails_root_join_argument_source(node, rails_root_index)
156
+ if rails_root_node.method?(:join)
157
+ append_argument(corrector, rails_root_node, argument_source)
158
+ else
159
+ replace_with_rails_root_join(corrector, rails_root_node, argument_source)
160
+ end
161
+ node.children[rails_root_index + 1..].each { |child| corrector.remove(child) }
162
+ end
163
+
164
+ def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
165
+ rails_root_node = node.children[rails_root_index].children.first
166
+ return unless rails_root_node.arguments.last.str_type?
167
+
168
+ corrector.insert_before(rails_root_node.arguments.last.location.end, extension_node.source)
169
+ corrector.remove(extension_node)
170
+ end
171
+
172
+ def autocorrect_file_join(corrector, node)
173
+ corrector.replace(node.receiver, 'Rails.root')
174
+ corrector.remove(
175
+ range_with_surrounding_space(
176
+ range_with_surrounding_comma(
177
+ node.arguments.first.source_range,
178
+ :right
179
+ ),
180
+ side: :right
181
+ )
182
+ )
183
+ corrector.insert_after(node, '.to_s')
184
+ end
185
+
186
+ def autocorrect_rails_root_join_with_string_arguments(corrector, node)
187
+ corrector.replace(node.arguments.first, %("#{node.arguments.map(&:value).join('/')}"))
188
+ node.arguments[1..].each do |argument|
189
+ corrector.remove(
190
+ range_with_surrounding_comma(
191
+ range_with_surrounding_space(
192
+ argument.source_range,
193
+ side: :left
194
+ ),
195
+ :left
196
+ )
197
+ )
198
+ end
199
+ end
200
+
201
+ def autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
202
+ node.arguments.each do |argument|
203
+ next unless string_with_slash?(argument)
204
+
205
+ index = argument.source.index(File::SEPARATOR)
206
+ rest = inner_range_of(argument).adjust(begin_pos: index - 1)
207
+ corrector.remove(rest)
208
+ corrector.insert_after(argument, %(, "#{rest.source.delete_prefix(File::SEPARATOR)}"))
209
+ end
210
+ end
211
+
212
+ def inner_range_of(node)
213
+ node.location.end.with(begin_pos: node.location.begin.end_pos).adjust(end_pos: -1)
214
+ end
215
+
216
+ def find_rails_root_index(node)
217
+ node.children.index { |child| rails_root_nodes?(child) }
218
+ end
219
+
220
+ def append_argument(corrector, node, argument_source)
221
+ corrector.insert_after(node.arguments.last, %(, "#{argument_source}"))
222
+ end
223
+
224
+ def replace_with_rails_root_join(corrector, node, argument_source)
225
+ corrector.replace(node, %<Rails.root.join("#{argument_source}")>)
226
+ end
227
+
228
+ def extract_rails_root_join_argument_source(node, rails_root_index)
229
+ node.children[rails_root_index + 1..].map(&:source).join.delete_prefix(File::SEPARATOR)
103
230
  end
104
231
 
105
- def message(_range)
106
- format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
232
+ def extension_node?(node)
233
+ node&.str_type? && node.source.start_with?('.')
107
234
  end
108
235
  end
109
236
  end
@@ -65,11 +65,11 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def where_take_offense_range(node, where)
68
- range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
68
+ range_between(where.loc.selector.begin_pos, node.source_range.end_pos)
69
69
  end
70
70
 
71
71
  def find_by_offense_range(node)
72
- range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
72
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
73
73
  end
74
74
 
75
75
  def build_good_method(id_value)
@@ -3,8 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Identifies usages of `all.each` and
7
- # change them to use `all.find_each` instead.
6
+ # Identifies usages of `all.each` and change them to use `all.find_each` instead.
7
+ #
8
+ # @safety
9
+ # This cop is unsafe if the receiver object is not an Active Record object.
10
+ # Also, `all.each` returns an `Array` instance and `all.find_each` returns nil,
11
+ # so the return values are different.
8
12
  #
9
13
  # @example
10
14
  # # bad
@@ -13,18 +17,24 @@ module RuboCop
13
17
  # # good
14
18
  # User.all.find_each
15
19
  #
16
- # @example IgnoredMethods: ['order']
20
+ # @example AllowedMethods: ['order']
21
+ # # good
22
+ # User.order(:foo).each
23
+ #
24
+ # @example AllowedPattern: ['order']
17
25
  # # good
18
26
  # User.order(:foo).each
19
27
  class FindEach < Base
20
28
  include ActiveRecordHelper
29
+ include AllowedMethods
30
+ include AllowedPattern
21
31
  extend AutoCorrector
22
32
 
23
33
  MSG = 'Use `find_each` instead of `each`.'
24
34
  RESTRICT_ON_SEND = %i[each].freeze
25
35
 
26
36
  SCOPE_METHODS = %i[
27
- all eager_load includes joins left_joins left_outer_joins not preload
37
+ all eager_load includes joins left_joins left_outer_joins not or preload
28
38
  references unscoped where
29
39
  ].freeze
30
40
 
@@ -47,7 +57,7 @@ module RuboCop
47
57
 
48
58
  method_chain = node.each_node(:send).map(&:method_name)
49
59
 
50
- (cop_config['IgnoredMethods'].map(&:to_sym) & method_chain).any?
60
+ method_chain.any? { |method_name| allowed_method?(method_name) || matches_allowed_pattern?(method_name) }
51
61
  end
52
62
 
53
63
  def active_model_error_where?(node)
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Identifies usages of `travel_to` with an argument of the current time and
7
+ # change them to use `freeze_time` instead.
8
+ #
9
+ # @safety
10
+ # This cop’s autocorrection is unsafe because `freeze_time` just delegates to
11
+ # `travel_to` with a default `Time.now`, it is not strictly equivalent to `Time.now`
12
+ # if the argument of `travel_to` is the current time considering time zone.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # travel_to(Time.now)
17
+ # travel_to(Time.new)
18
+ # travel_to(DateTime.now)
19
+ # travel_to(Time.current)
20
+ # travel_to(Time.zone.now)
21
+ # travel_to(Time.now.in_time_zone)
22
+ # travel_to(Time.current.to_time)
23
+ #
24
+ # # good
25
+ # freeze_time
26
+ #
27
+ class FreezeTime < Base
28
+ extend AutoCorrector
29
+ extend TargetRailsVersion
30
+
31
+ minimum_target_rails_version 5.2
32
+
33
+ MSG = 'Use `freeze_time` instead of `travel_to`.'
34
+ NOW_METHODS = %i[now new current].freeze
35
+ CONVERT_METHODS = %i[to_time in_time_zone].freeze
36
+ RESTRICT_ON_SEND = %i[travel_to].freeze
37
+
38
+ # @!method time_now?(node)
39
+ def_node_matcher :time_now?, <<~PATTERN
40
+ (const {nil? cbase} {:Time :DateTime})
41
+ PATTERN
42
+
43
+ # @!method zoned_time_now?(node)
44
+ def_node_matcher :zoned_time_now?, <<~PATTERN
45
+ (send (const {nil? cbase} :Time) :zone)
46
+ PATTERN
47
+
48
+ def on_send(node)
49
+ child_node, method_name, time_argument = *node.first_argument&.children
50
+ return if time_argument || !child_node
51
+ return unless current_time?(child_node, method_name) || current_time_with_convert?(child_node, method_name)
52
+
53
+ add_offense(node) do |corrector|
54
+ last_argument = node.last_argument
55
+ freeze_time_method = last_argument.block_pass_type? ? "freeze_time(#{last_argument.source})" : 'freeze_time'
56
+ corrector.replace(node, freeze_time_method)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def current_time?(node, method_name)
63
+ return false unless NOW_METHODS.include?(method_name)
64
+
65
+ node.send_type? ? zoned_time_now?(node) : time_now?(node)
66
+ end
67
+
68
+ def current_time_with_convert?(node, method_name)
69
+ return false unless CONVERT_METHODS.include?(method_name)
70
+
71
+ child_node, child_method_name, time_argument = *node.children
72
+ return if time_argument
73
+
74
+ current_time?(child_node, child_method_name)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -37,15 +37,15 @@ module RuboCop
37
37
  RESTRICT_ON_SEND = %i[has_many has_one].freeze
38
38
 
39
39
  def_node_search :active_resource_class?, <<~PATTERN
40
- (const (const nil? :ActiveResource) :Base)
40
+ (const (const {nil? cbase} :ActiveResource) :Base)
41
41
  PATTERN
42
42
 
43
43
  def_node_matcher :association_without_options?, <<~PATTERN
44
- (send nil? {:has_many :has_one} _)
44
+ (send _ {:has_many :has_one} _)
45
45
  PATTERN
46
46
 
47
47
  def_node_matcher :association_with_options?, <<~PATTERN
48
- (send nil? {:has_many :has_one} ... (hash $...))
48
+ (send _ {:has_many :has_one} ... (hash $...))
49
49
  PATTERN
50
50
 
51
51
  def_node_matcher :dependent_option?, <<~PATTERN
@@ -105,9 +105,7 @@ module RuboCop
105
105
 
106
106
  return false unless node.parent
107
107
 
108
- return true if contain_valid_options_in_with_options_block?(
109
- node.parent.parent
110
- )
108
+ return true if contain_valid_options_in_with_options_block?(node.parent.parent)
111
109
  end
112
110
 
113
111
  false
@@ -37,7 +37,7 @@ module RuboCop
37
37
  def_node_matcher :form_builder_class?, <<~PATTERN
38
38
  (const
39
39
  (const
40
- (const nil? :ActionView) :Helpers) :FormBuilder)
40
+ (const {nil? cbase} :ActionView) :Helpers) :FormBuilder)
41
41
  PATTERN
42
42
 
43
43
  def on_ivar(node)
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your
11
11
  # .rubocop.yml file to 4.2.
12
12
  #
13
+ # NOTE: It does not detect any cases where `include Rack::Test::Methods` is used
14
+ # which makes the http methods incompatible behavior.
15
+ #
13
16
  # @example
14
17
  # # bad
15
18
  # get :new, { user_id: 1}
@@ -22,11 +25,8 @@ module RuboCop
22
25
  extend AutoCorrector
23
26
  extend TargetRailsVersion
24
27
 
25
- MSG = 'Use keyword arguments instead of ' \
26
- 'positional arguments for http call: `%<verb>s`.'
27
- KEYWORD_ARGS = %i[
28
- method params session body flash xhr as headers env to
29
- ].freeze
28
+ MSG = 'Use keyword arguments instead of positional arguments for http call: `%<verb>s`.'
29
+ KEYWORD_ARGS = %i[method params session body flash xhr as headers env to].freeze
30
30
  ROUTING_METHODS = %i[draw routes].freeze
31
31
  RESTRICT_ON_SEND = %i[get post put patch delete head].freeze
32
32
 
@@ -40,8 +40,15 @@ module RuboCop
40
40
  (hash (kwsplat _))
41
41
  PATTERN
42
42
 
43
+ def_node_matcher :include_rack_test_methods?, <<~PATTERN
44
+ (send nil? :include
45
+ (const
46
+ (const
47
+ (const {nil? cbase} :Rack) :Test) :Methods))
48
+ PATTERN
49
+
43
50
  def on_send(node)
44
- return if in_routing_block?(node)
51
+ return if in_routing_block?(node) || use_rack_test_methods?
45
52
 
46
53
  http_request?(node) do |data|
47
54
  return unless needs_conversion?(data)
@@ -59,7 +66,7 @@ module RuboCop
59
66
  # that represents the path/action on the Rails controller
60
67
  # the data is the http parameters and environment sent in
61
68
  # the Rails 5 http call
62
- corrector.replace(node.loc.expression, correction(node))
69
+ corrector.replace(node, correction(node))
63
70
  end
64
71
  end
65
72
  end
@@ -70,13 +77,18 @@ module RuboCop
70
77
  !!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.method_name) }
71
78
  end
72
79
 
80
+ def use_rack_test_methods?
81
+ processed_source.ast.each_descendant(:send).any? do |node|
82
+ include_rack_test_methods?(node)
83
+ end
84
+ end
85
+
73
86
  def needs_conversion?(data)
74
87
  return true unless data.hash_type?
75
88
  return false if kwsplat_hash?(data)
76
89
 
77
90
  data.each_pair.none? do |pair|
78
- special_keyword_arg?(pair.key) ||
79
- (format_arg?(pair.key) && data.pairs.one?)
91
+ special_keyword_arg?(pair.key) || (format_arg?(pair.key) && data.pairs.one?)
80
92
  end
81
93
  end
82
94
 
@@ -98,8 +110,7 @@ module RuboCop
98
110
  return '' if data.hash_type? && data.empty?
99
111
 
100
112
  hash_data = if data.hash_type?
101
- format('{ %<data>s }',
102
- data: data.pairs.map(&:source).join(', '))
113
+ format('{ %<data>s }', data: data.pairs.map(&:source).join(', '))
103
114
  else
104
115
  # user supplies an object,
105
116
  # no need to surround with braces
@@ -66,7 +66,7 @@ module RuboCop
66
66
  return unless checker.offensive?
67
67
 
68
68
  add_offense(checker.node, message: checker.message) do |corrector|
69
- corrector.replace(checker.node.loc.expression, checker.preferred_style)
69
+ corrector.replace(checker.node, checker.preferred_style)
70
70
  end
71
71
  end
72
72
  end
@@ -84,10 +84,8 @@ module RuboCop
84
84
 
85
85
  # :nodoc:
86
86
  class SymbolicStyleChecker
87
- MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
88
- 'to define HTTP status code.'
89
- DEFAULT_MSG = 'Prefer `symbolic` over `numeric` ' \
90
- 'to define HTTP status code.'
87
+ MSG = 'Prefer `%<prefer>s` over `%<current>s` to define HTTP status code.'
88
+ DEFAULT_MSG = 'Prefer `symbolic` over `numeric` to define HTTP status code.'
91
89
 
92
90
  attr_reader :node
93
91
 
@@ -118,17 +116,14 @@ module RuboCop
118
116
  end
119
117
 
120
118
  def custom_http_status_code?
121
- node.int_type? &&
122
- !::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(number)
119
+ node.int_type? && !::Rack::Utils::SYMBOL_TO_STATUS_CODE.value?(number)
123
120
  end
124
121
  end
125
122
 
126
123
  # :nodoc:
127
124
  class NumericStyleChecker
128
- MSG = 'Prefer `%<prefer>s` over `%<current>s` ' \
129
- 'to define HTTP status code.'
130
- DEFAULT_MSG = 'Prefer `numeric` over `symbolic` ' \
131
- 'to define HTTP status code.'
125
+ MSG = 'Prefer `%<prefer>s` over `%<current>s` to define HTTP status code.'
126
+ DEFAULT_MSG = 'Prefer `numeric` over `symbolic` to define HTTP status code.'
132
127
  PERMITTED_STATUS = %i[error success missing redirect].freeze
133
128
 
134
129
  attr_reader :node
@@ -34,6 +34,8 @@ module RuboCop
34
34
 
35
35
  MSG = 'Use "lazy" lookup for the text used in controllers.'
36
36
 
37
+ RESTRICT_ON_SEND = %i[translate t].freeze
38
+
37
39
  def_node_matcher :translate_call?, <<~PATTERN
38
40
  (send nil? {:translate :t} ${sym_type? str_type?} ...)
39
41
  PATTERN
@@ -69,7 +69,7 @@ module RuboCop
69
69
  class I18nLocaleTexts < Base
70
70
  MSG = 'Move locale texts to the locale files in the `config/locales` directory.'
71
71
 
72
- RESTRICT_ON_SEND = %i[validates redirect_to []= mail].freeze
72
+ RESTRICT_ON_SEND = %i[validates redirect_to redirect_back []= mail].freeze
73
73
 
74
74
  def_node_search :validation_message, <<~PATTERN
75
75
  (pair (sym :message) $str)
@@ -80,7 +80,11 @@ module RuboCop
80
80
  PATTERN
81
81
 
82
82
  def_node_matcher :flash_assignment?, <<~PATTERN
83
- (send (send nil? :flash) :[]= _ $str)
83
+ (send
84
+ {
85
+ (send nil? :flash)
86
+ (send (send nil? :flash) :now)
87
+ } :[]= _ $str)
84
88
  PATTERN
85
89
 
86
90
  def_node_search :mail_subject, <<~PATTERN
@@ -94,7 +98,7 @@ module RuboCop
94
98
  add_offense(text_node)
95
99
  end
96
100
  return
97
- when :redirect_to
101
+ when :redirect_to, :redirect_back
98
102
  text_node = redirect_to_flash(node).to_a.last
99
103
  when :[]=
100
104
  text_node = flash_assignment?(node)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Looks for assignments of `ignored_columns` that may override previous
7
+ # assignments.
8
+ #
9
+ # Overwriting previous assignments is usually a mistake, since it will
10
+ # un-ignore the first set of columns. Since duplicate column names is not
11
+ # a problem, it is better to simply append to the list.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # class User < ActiveRecord::Base
17
+ # self.ignored_columns = [:one]
18
+ # end
19
+ #
20
+ # # bad
21
+ # class User < ActiveRecord::Base
22
+ # self.ignored_columns = [:one, :two]
23
+ # end
24
+ #
25
+ # # good
26
+ # class User < ActiveRecord::Base
27
+ # self.ignored_columns += [:one, :two]
28
+ # end
29
+ #
30
+ # # good
31
+ # class User < ActiveRecord::Base
32
+ # self.ignored_columns += [:one]
33
+ # self.ignored_columns += [:two]
34
+ # end
35
+ #
36
+ class IgnoredColumnsAssignment < Base
37
+ extend AutoCorrector
38
+
39
+ MSG = 'Use `+=` instead of `=`.'
40
+ RESTRICT_ON_SEND = %i[ignored_columns=].freeze
41
+
42
+ def on_send(node)
43
+ add_offense(node.loc.operator) do |corrector|
44
+ corrector.replace(node.loc.operator, '+=')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end