rubocop-rails 2.14.2 → 2.19.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +23 -2
  4. data/config/default.yml +190 -12
  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 +2 -2
  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 +7 -4
  17. data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
  18. data/lib/rubocop/cop/rails/active_support_aliases.rb +1 -1
  19. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  20. data/lib/rubocop/cop/rails/add_column_index.rb +3 -6
  21. data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
  22. data/lib/rubocop/cop/rails/application_controller.rb +2 -2
  23. data/lib/rubocop/cop/rails/application_job.rb +3 -3
  24. data/lib/rubocop/cop/rails/application_mailer.rb +2 -2
  25. data/lib/rubocop/cop/rails/application_record.rb +2 -2
  26. data/lib/rubocop/cop/rails/arel_star.rb +2 -2
  27. data/lib/rubocop/cop/rails/assert_not.rb +1 -1
  28. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +1 -1
  29. data/lib/rubocop/cop/rails/belongs_to.rb +2 -5
  30. data/lib/rubocop/cop/rails/blank.rb +10 -11
  31. data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -25
  32. data/lib/rubocop/cop/rails/compact_blank.rb +6 -2
  33. data/lib/rubocop/cop/rails/content_tag.rb +6 -7
  34. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +16 -3
  35. data/lib/rubocop/cop/rails/date.rb +12 -17
  36. data/lib/rubocop/cop/rails/default_scope.rb +1 -1
  37. data/lib/rubocop/cop/rails/delegate.rb +24 -18
  38. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +2 -2
  39. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +63 -3
  40. data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
  41. data/lib/rubocop/cop/rails/duplicate_association.rb +2 -2
  42. data/lib/rubocop/cop/rails/duplicate_scope.rb +1 -1
  43. data/lib/rubocop/cop/rails/duration_arithmetic.rb +4 -4
  44. data/lib/rubocop/cop/rails/dynamic_find_by.rb +26 -14
  45. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +6 -2
  46. data/lib/rubocop/cop/rails/enum_hash.rb +3 -4
  47. data/lib/rubocop/cop/rails/enum_uniqueness.rb +3 -6
  48. data/lib/rubocop/cop/rails/environment_comparison.rb +3 -4
  49. data/lib/rubocop/cop/rails/environment_variable_access.rb +1 -1
  50. data/lib/rubocop/cop/rails/exit.rb +1 -1
  51. data/lib/rubocop/cop/rails/expanded_date_range.rb +39 -23
  52. data/lib/rubocop/cop/rails/file_path.rb +41 -24
  53. data/lib/rubocop/cop/rails/find_by.rb +1 -1
  54. data/lib/rubocop/cop/rails/find_by_id.rb +3 -3
  55. data/lib/rubocop/cop/rails/find_each.rb +14 -4
  56. data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
  57. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +1 -1
  58. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +14 -8
  59. data/lib/rubocop/cop/rails/helper_instance_variable.rb +3 -3
  60. data/lib/rubocop/cop/rails/http_positional_arguments.rb +23 -12
  61. data/lib/rubocop/cop/rails/http_status.rb +6 -11
  62. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +3 -1
  63. data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +1 -1
  64. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +2 -2
  65. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  66. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +5 -14
  67. data/lib/rubocop/cop/rails/index_by.rb +2 -2
  68. data/lib/rubocop/cop/rails/index_with.rb +2 -2
  69. data/lib/rubocop/cop/rails/inquiry.rb +1 -1
  70. data/lib/rubocop/cop/rails/inverse_of.rb +4 -10
  71. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +22 -16
  72. data/lib/rubocop/cop/rails/link_to_blank.rb +2 -5
  73. data/lib/rubocop/cop/rails/mailer_name.rb +5 -5
  74. data/lib/rubocop/cop/rails/match_route.rb +1 -1
  75. data/lib/rubocop/cop/rails/migration_class_name.rb +2 -2
  76. data/lib/rubocop/cop/rails/negate_include.rb +2 -2
  77. data/lib/rubocop/cop/rails/not_null_column.rb +10 -7
  78. data/lib/rubocop/cop/rails/order_by_id.rb +2 -3
  79. data/lib/rubocop/cop/rails/output.rb +7 -9
  80. data/lib/rubocop/cop/rails/output_safety.rb +6 -2
  81. data/lib/rubocop/cop/rails/pick.rb +1 -1
  82. data/lib/rubocop/cop/rails/pluck.rb +45 -13
  83. data/lib/rubocop/cop/rails/pluck_id.rb +2 -2
  84. data/lib/rubocop/cop/rails/pluck_in_where.rb +1 -1
  85. data/lib/rubocop/cop/rails/pluralization_grammar.rb +2 -3
  86. data/lib/rubocop/cop/rails/presence.rb +22 -13
  87. data/lib/rubocop/cop/rails/present.rb +10 -13
  88. data/lib/rubocop/cop/rails/rake_environment.rb +3 -3
  89. data/lib/rubocop/cop/rails/read_write_attribute.rb +2 -2
  90. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +5 -7
  91. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +3 -3
  92. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
  93. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +31 -27
  94. data/lib/rubocop/cop/rails/redundant_travel_back.rb +1 -1
  95. data/lib/rubocop/cop/rails/reflection_class_name.rb +35 -2
  96. data/lib/rubocop/cop/rails/refute_methods.rb +1 -5
  97. data/lib/rubocop/cop/rails/relative_date_constant.rb +5 -8
  98. data/lib/rubocop/cop/rails/render_inline.rb +1 -1
  99. data/lib/rubocop/cop/rails/render_plain_text.rb +1 -1
  100. data/lib/rubocop/cop/rails/request_referer.rb +2 -3
  101. data/lib/rubocop/cop/rails/require_dependency.rb +2 -2
  102. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  103. data/lib/rubocop/cop/rails/reversible_migration.rb +15 -63
  104. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +5 -6
  105. data/lib/rubocop/cop/rails/root_join_chain.rb +1 -1
  106. data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
  107. data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
  108. data/lib/rubocop/cop/rails/safe_navigation.rb +8 -13
  109. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +2 -4
  110. data/lib/rubocop/cop/rails/save_bang.rb +12 -24
  111. data/lib/rubocop/cop/rails/schema_comment.rb +1 -1
  112. data/lib/rubocop/cop/rails/scope_args.rb +1 -1
  113. data/lib/rubocop/cop/rails/short_i18n.rb +3 -6
  114. data/lib/rubocop/cop/rails/skips_model_validations.rb +3 -4
  115. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +10 -7
  116. data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
  117. data/lib/rubocop/cop/rails/table_name_assignment.rb +1 -1
  118. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
  119. data/lib/rubocop/cop/rails/time_zone.rb +34 -32
  120. data/lib/rubocop/cop/rails/time_zone_assignment.rb +4 -4
  121. data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
  122. data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
  123. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  124. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +17 -12
  125. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
  126. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +14 -7
  127. data/lib/rubocop/cop/rails/unknown_env.rb +3 -5
  128. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +7 -2
  129. data/lib/rubocop/cop/rails/validation.rb +5 -13
  130. data/lib/rubocop/cop/rails/where_equals.rb +2 -2
  131. data/lib/rubocop/cop/rails/where_exists.rb +3 -3
  132. data/lib/rubocop/cop/rails/where_missing.rb +118 -0
  133. data/lib/rubocop/cop/rails/where_not.rb +2 -2
  134. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  135. data/lib/rubocop/cop/rails_cops.rb +16 -0
  136. data/lib/rubocop/rails/schema_loader/schema.rb +8 -5
  137. data/lib/rubocop/rails/version.rb +1 -1
  138. data/lib/rubocop/rails.rb +1 -1
  139. data/lib/rubocop-rails.rb +19 -0
  140. metadata +23 -9
  141. data/bin/console +0 -11
  142. data/bin/setup +0 -7
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks dynamic `find_by_*` methods.
6
+ # Checks dynamic `find_by_*` methods.
7
7
  # Use `find_by` instead of dynamic method.
8
8
  # See. https://rails.rubystyle.guide#find_by
9
9
  #
@@ -22,19 +22,23 @@ module RuboCop
22
22
  # User.find_by(name: name, email: email)
23
23
  # User.find_by!(email: email)
24
24
  #
25
- # @example AllowedMethods: find_by_sql
25
+ # @example AllowedMethods: ['find_by_sql', 'find_by_token_for'] (default)
26
26
  # # bad
27
27
  # User.find_by_query(users_query)
28
+ # User.find_by_token_for(:password_reset, token)
28
29
  #
29
30
  # # good
30
31
  # User.find_by_sql(users_sql)
32
+ # User.find_by_token_for(:password_reset, token)
31
33
  #
32
- # @example AllowedReceivers: Gem::Specification
34
+ # @example AllowedReceivers: ['Gem::Specification', 'page'] (default)
33
35
  # # bad
34
36
  # Specification.find_by_name('backend').gem_dir
37
+ # page.find_by_id('a_dom_id').click
35
38
  #
36
39
  # # good
37
40
  # Gem::Specification.find_by_name('backend').gem_dir
41
+ # page.find_by_id('a_dom_id').click
38
42
  class DynamicFindBy < Base
39
43
  include ActiveRecordHelper
40
44
  extend AutoCorrector
@@ -49,7 +53,7 @@ module RuboCop
49
53
  method_name = node.method_name
50
54
  static_name = static_method_name(method_name)
51
55
  return unless static_name
52
- return if node.arguments.any? { |argument| IGNORED_ARGUMENT_TYPES.include?(argument.type) }
56
+ return unless dynamic_find_by_arguments?(node)
53
57
 
54
58
  message = format(MSG, static_name: static_name, method: method_name)
55
59
  add_offense(node, message: message) do |corrector|
@@ -61,17 +65,12 @@ module RuboCop
61
65
  private
62
66
 
63
67
  def autocorrect(corrector, node)
64
- keywords = column_keywords(node.method_name)
65
-
66
- return if keywords.size != node.arguments.size
67
-
68
68
  autocorrect_method_name(corrector, node)
69
- autocorrect_argument_keywords(corrector, node, keywords)
69
+ autocorrect_argument_keywords(corrector, node, column_keywords(node.method_name))
70
70
  end
71
71
 
72
72
  def allowed_invocation?(node)
73
- allowed_method?(node) || allowed_receiver?(node) ||
74
- whitelisted?(node)
73
+ allowed_method?(node) || allowed_receiver?(node) || whitelisted?(node)
75
74
  end
76
75
 
77
76
  def allowed_method?(node)
@@ -95,13 +94,12 @@ module RuboCop
95
94
  end
96
95
 
97
96
  def autocorrect_method_name(corrector, node)
98
- corrector.replace(node.loc.selector,
99
- static_method_name(node.method_name.to_s))
97
+ corrector.replace(node.loc.selector, static_method_name(node.method_name.to_s))
100
98
  end
101
99
 
102
100
  def autocorrect_argument_keywords(corrector, node, keywords)
103
101
  keywords.each.with_index do |keyword, idx|
104
- corrector.insert_before(node.arguments[idx].loc.expression, keyword)
102
+ corrector.insert_before(node.arguments[idx], keyword)
105
103
  end
106
104
  end
107
105
 
@@ -118,6 +116,20 @@ module RuboCop
118
116
 
119
117
  match[2] ? 'find_by!' : 'find_by'
120
118
  end
119
+
120
+ def dynamic_find_by_arguments?(node)
121
+ dynamic_find_by_arguments_count?(node) && dynamic_find_by_arguments_type?(node)
122
+ end
123
+
124
+ def dynamic_find_by_arguments_count?(node)
125
+ column_keywords(node.method_name).size == node.arguments.size
126
+ end
127
+
128
+ def dynamic_find_by_arguments_type?(node)
129
+ node.arguments.none? do |argument|
130
+ IGNORED_ARGUMENT_TYPES.include?(argument.type)
131
+ end
132
+ end
121
133
  end
122
134
  end
123
135
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that blocks are used for interpolated strings passed to
6
+ # Checks that blocks are used for interpolated strings passed to
7
7
  # `Rails.logger.debug`.
8
8
  #
9
9
  # By default, Rails production environments use the `:info` log level.
@@ -37,6 +37,10 @@ module RuboCop
37
37
  )
38
38
  PATTERN
39
39
 
40
+ def self.autocorrect_incompatible_with
41
+ [Style::MethodCallWithArgsParentheses]
42
+ end
43
+
40
44
  def on_send(node)
41
45
  return if node.parent&.block_type?
42
46
 
@@ -55,7 +59,7 @@ module RuboCop
55
59
  private
56
60
 
57
61
  def replacement_range(node)
58
- stop = node.loc.expression.end
62
+ stop = node.source_range.end
59
63
  start = node.loc.selector.end
60
64
 
61
65
  if node.parenthesized_call?
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for enums written with array syntax.
6
+ # Looks for enums written with array syntax.
7
7
  #
8
8
  # When using array syntax, adding an element in a
9
9
  # position other than the last causes all previous
@@ -20,8 +20,7 @@ module RuboCop
20
20
  class EnumHash < Base
21
21
  extend AutoCorrector
22
22
 
23
- MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
24
- 'Use hash syntax instead.'
23
+ MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
25
24
  RESTRICT_ON_SEND = %i[enum].freeze
26
25
 
27
26
  def_node_matcher :enum?, <<~PATTERN
@@ -43,7 +42,7 @@ module RuboCop
43
42
  "#{source(elem)} => #{index}"
44
43
  end.join(', ')
45
44
 
46
- corrector.replace(array.loc.expression, "{#{hash}}")
45
+ corrector.replace(array, "{#{hash}}")
47
46
  end
48
47
  end
49
48
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for duplicate values in enum declarations.
6
+ # Looks for duplicate values in enum declarations.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -20,8 +20,7 @@ module RuboCop
20
20
  class EnumUniqueness < Base
21
21
  include Duplication
22
22
 
23
- MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
24
- 'enum declaration.'
23
+ MSG = 'Duplicate value `%<value>s` found in `%<enum>s` enum declaration.'
25
24
  RESTRICT_ON_SEND = %i[enum].freeze
26
25
 
27
26
  def_node_matcher :enum?, <<~PATTERN
@@ -41,9 +40,7 @@ module RuboCop
41
40
  next unless duplicates?(items)
42
41
 
43
42
  consecutive_duplicates(items).each do |item|
44
- add_offense(item, message: format(
45
- MSG, value: item.source, enum: enum_name(key)
46
- ))
43
+ add_offense(item, message: format(MSG, value: item.source, enum: enum_name(key)))
47
44
  end
48
45
  end
49
46
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that Rails.env is compared using `.production?`-like
6
+ # Checks that Rails.env is compared using `.production?`-like
7
7
  # methods instead of equality against a string or symbol.
8
8
  #
9
9
  # @example
@@ -84,7 +84,7 @@ module RuboCop
84
84
  def autocorrect(corrector, node)
85
85
  replacement = build_predicate_method(node)
86
86
 
87
- corrector.replace(node.source_range, replacement)
87
+ corrector.replace(node, replacement)
88
88
  end
89
89
 
90
90
  def build_predicate_method(node)
@@ -96,8 +96,7 @@ module RuboCop
96
96
  end
97
97
 
98
98
  def rails_env_on_lhs?(node)
99
- comparing_str_env_with_rails_env_on_lhs?(node) ||
100
- comparing_sym_env_with_rails_env_on_lhs?(node)
99
+ comparing_str_env_with_rails_env_on_lhs?(node) || comparing_sym_env_with_rails_env_on_lhs?(node)
101
100
  end
102
101
 
103
102
  def build_predicate_method_for_rails_env_on_lhs(node)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for direct access to environment variables through the
6
+ # Looks for direct access to environment variables through the
7
7
  # `ENV` variable within the application code. This can lead to runtime
8
8
  # errors due to misconfiguration that could have been discovered at boot
9
9
  # time if the environment variables were loaded as part of initialization
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop enforces that `exit` calls are not used within a rails app.
6
+ # Enforces that `exit` calls are not used within a rails app.
7
7
  # Valid options are instead to raise an error, break, return, or some
8
8
  # other form of stopping execution of current request.
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks for expanded date range. It only compatible `..` range is targeted.
6
+ # Checks for expanded date range. It only compatible `..` range is targeted.
7
7
  # Incompatible `...` range is ignored.
8
8
  #
9
9
  # @example
@@ -29,14 +29,6 @@ module RuboCop
29
29
 
30
30
  minimum_target_rails_version 5.1
31
31
 
32
- def_node_matcher :expanded_date_range, <<~PATTERN
33
- (irange
34
- (send
35
- $_ {:beginning_of_day :beginning_of_week :beginning_of_month :beginning_of_quarter :beginning_of_year})
36
- (send
37
- $_ {:end_of_day :end_of_week :end_of_month :end_of_quarter :end_of_year}))
38
- PATTERN
39
-
40
32
  PREFERRED_METHODS = {
41
33
  beginning_of_day: 'all_day',
42
34
  beginning_of_week: 'all_week',
@@ -54,31 +46,55 @@ module RuboCop
54
46
  }.freeze
55
47
 
56
48
  def on_irange(node)
57
- return unless expanded_date_range(node)
58
-
59
49
  begin_node = node.begin
60
50
  end_node = node.end
61
- return unless same_receiver?(begin_node, end_node)
62
-
63
- beginning_method = begin_node.method_name
64
- end_method = end_node.method_name
65
- return unless use_mapped_methods?(beginning_method, end_method)
51
+ return if allow?(begin_node, end_node)
66
52
 
67
- preferred_method = "#{begin_node.receiver.source}.#{PREFERRED_METHODS[beginning_method]}"
53
+ preferred_method = preferred_method(begin_node)
54
+ if begin_node.method?(:beginning_of_week) && begin_node.arguments.one?
55
+ return unless same_argument?(begin_node, end_node)
68
56
 
69
- add_offense(node, message: format(MSG, preferred_method: preferred_method)) do |corrector|
70
- corrector.replace(node, preferred_method)
57
+ preferred_method << "(#{begin_node.first_argument.source})"
58
+ elsif any_arguments?(begin_node, end_node)
59
+ return
71
60
  end
61
+
62
+ register_offense(node, preferred_method)
72
63
  end
73
64
 
74
65
  private
75
66
 
76
- def same_receiver?(begin_node, end_node)
77
- begin_node.receiver.source == end_node.receiver.source
67
+ def allow?(begin_node, end_node)
68
+ return true unless (begin_source = receiver_source(begin_node))
69
+ return true unless (end_source = receiver_source(end_node))
70
+
71
+ begin_source != end_source || MAPPED_DATE_RANGE_METHODS[begin_node.method_name] != end_node.method_name
72
+ end
73
+
74
+ def receiver_source(node)
75
+ return if !node&.send_type? || node.receiver.nil?
76
+
77
+ node.receiver.source
78
78
  end
79
79
 
80
- def use_mapped_methods?(beginning_method, end_method)
81
- MAPPED_DATE_RANGE_METHODS[beginning_method] == end_method
80
+ def same_argument?(begin_node, end_node)
81
+ begin_node.first_argument.source == end_node.first_argument.source
82
+ end
83
+
84
+ def preferred_method(begin_node)
85
+ +"#{begin_node.receiver.source}.#{PREFERRED_METHODS[begin_node.method_name]}"
86
+ end
87
+
88
+ def any_arguments?(begin_node, end_node)
89
+ begin_node.arguments.any? || end_node.arguments.any?
90
+ end
91
+
92
+ def register_offense(node, preferred_method)
93
+ message = format(MSG, preferred_method: preferred_method)
94
+
95
+ add_offense(node, message: message) do |corrector|
96
+ corrector.replace(node, preferred_method)
97
+ end
82
98
  end
83
99
  end
84
100
  end
@@ -3,55 +3,66 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop is used to identify 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
- # @example EnforcedStyle: arguments
9
+ # @example EnforcedStyle: slashes (default)
11
10
  # # bad
11
+ # Rails.root.join('app', 'models', 'goober')
12
+ #
13
+ # # good
12
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
- # @example EnforcedStyle: slashes (default)
23
+ # @example EnforcedStyle: arguments
20
24
  # # bad
25
+ # Rails.root.join('app/models/goober')
26
+ #
27
+ # # good
21
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
29
38
  include ConfigurableEnforcedStyle
30
39
  include RangeHelp
31
40
 
32
- MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')`.'
33
- MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')`.'
41
+ MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')%<to_s>s`.'
42
+ MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')%<to_s>s`.'
34
43
  RESTRICT_ON_SEND = %i[join].freeze
35
44
 
36
45
  def_node_matcher :file_join_nodes?, <<~PATTERN
37
- (send (const nil? :File) :join ...)
46
+ (send (const {nil? cbase} :File) :join ...)
38
47
  PATTERN
39
48
 
40
49
  def_node_search :rails_root_nodes?, <<~PATTERN
41
- (send (const nil? :Rails) :root)
50
+ (send (const {nil? cbase} :Rails) :root)
42
51
  PATTERN
43
52
 
44
53
  def_node_matcher :rails_root_join_nodes?, <<~PATTERN
45
- (send (send (const nil? :Rails) :root) :join ...)
54
+ (send #rails_root_nodes? :join ...)
46
55
  PATTERN
47
56
 
48
57
  def on_dstr(node)
49
58
  return unless rails_root_nodes?(node)
50
59
  return unless node.children.last.str_type?
51
- return unless node.children.last.source.start_with?('.') ||
52
- node.children.last.source.include?(File::SEPARATOR)
53
60
 
54
- register_offense(node)
61
+ last_child_source = node.children.last.source
62
+ return unless last_child_source.start_with?('.') || last_child_source.include?(File::SEPARATOR)
63
+ return if last_child_source.start_with?(':')
64
+
65
+ register_offense(node, require_to_s: true)
55
66
  end
56
67
 
57
68
  def on_send(node)
@@ -66,7 +77,7 @@ module RuboCop
66
77
  return unless file_join_nodes?(node)
67
78
  return unless node.arguments.any? { |e| rails_root_nodes?(e) }
68
79
 
69
- register_offense(node)
80
+ register_offense(node, require_to_s: true)
70
81
  end
71
82
 
72
83
  def check_for_rails_root_join_with_string_arguments(node)
@@ -76,7 +87,7 @@ module RuboCop
76
87
  return unless node.arguments.size > 1
77
88
  return unless node.arguments.all?(&:str_type?)
78
89
 
79
- register_offense(node)
90
+ register_offense(node, require_to_s: false)
80
91
  end
81
92
 
82
93
  def check_for_rails_root_join_with_slash_separated_path(node)
@@ -85,22 +96,28 @@ module RuboCop
85
96
  return unless rails_root_join_nodes?(node)
86
97
  return unless node.arguments.any? { |arg| string_with_slash?(arg) }
87
98
 
88
- register_offense(node)
99
+ register_offense(node, require_to_s: false)
89
100
  end
90
101
 
91
102
  def string_with_slash?(node)
92
103
  node.str_type? && node.source.include?('/')
93
104
  end
94
105
 
95
- def register_offense(node)
106
+ def register_offense(node, require_to_s:)
96
107
  line_range = node.loc.column...node.loc.last_column
97
- source_range = source_range(processed_source.buffer, node.first_line,
98
- line_range)
99
- add_offense(source_range)
108
+ source_range = source_range(processed_source.buffer, node.first_line, line_range)
109
+ require_to_s = false if node.dstr_type?
110
+
111
+ message = build_message(require_to_s)
112
+
113
+ add_offense(source_range, message: message)
100
114
  end
101
115
 
102
- def message(_range)
103
- format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
116
+ def build_message(require_to_s)
117
+ message_template = style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES
118
+ to_s = require_to_s ? '.to_s' : ''
119
+
120
+ format(message_template, to_s: to_s)
104
121
  end
105
122
  end
106
123
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop is used to identify usages of `where.take` and change them to use `find_by` instead.
6
+ # Identifies usages of `where.take` and change them to use `find_by` instead.
7
7
  #
8
8
  # And `where(...).first` can return different results from `find_by`.
9
9
  # (They order records differently, so the "first" record can be different.)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop enforces that `ActiveRecord#find` is used instead of
6
+ # Enforces that `ActiveRecord#find` is used instead of
7
7
  # `where.take!`, `find_by!`, and `find_by_id!` to retrieve a single record
8
8
  # by primary key when you expect it to be found.
9
9
  #
@@ -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
- # This cop is used to identify 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,11 +17,17 @@ 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`.'
@@ -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
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks for the use of the has_and_belongs_to_many macro.
6
+ # Checks for the use of the has_and_belongs_to_many macro.
7
7
  #
8
8
  # @example
9
9
  # # bad