rubocop-rails 2.0.1 → 2.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -5
  4. data/config/default.yml +726 -32
  5. data/config/obsoletion.yml +17 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +106 -0
  7. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +32 -0
  8. data/lib/rubocop/cop/mixin/class_send_node_helper.rb +20 -0
  9. data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
  10. data/lib/rubocop/cop/mixin/index_method.rb +165 -0
  11. data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
  12. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
  13. data/lib/rubocop/cop/rails/action_controller_test_case.rb +47 -0
  14. data/lib/rubocop/cop/rails/action_filter.rb +11 -21
  15. data/lib/rubocop/cop/rails/action_order.rb +116 -0
  16. data/lib/rubocop/cop/rails/active_record_aliases.rb +23 -24
  17. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +143 -0
  18. data/lib/rubocop/cop/rails/active_record_override.rb +3 -6
  19. data/lib/rubocop/cop/rails/active_support_aliases.rb +13 -22
  20. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  21. data/lib/rubocop/cop/rails/add_column_index.rb +61 -0
  22. data/lib/rubocop/cop/rails/after_commit_override.rb +81 -0
  23. data/lib/rubocop/cop/rails/application_controller.rb +36 -0
  24. data/lib/rubocop/cop/rails/application_job.rb +9 -4
  25. data/lib/rubocop/cop/rails/application_mailer.rb +39 -0
  26. data/lib/rubocop/cop/rails/application_record.rb +9 -9
  27. data/lib/rubocop/cop/rails/arel_star.rb +47 -0
  28. data/lib/rubocop/cop/rails/assert_not.rb +8 -10
  29. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
  30. data/lib/rubocop/cop/rails/belongs_to.rb +12 -24
  31. data/lib/rubocop/cop/rails/blank.rb +40 -36
  32. data/lib/rubocop/cop/rails/bulk_change_table.rb +40 -35
  33. data/lib/rubocop/cop/rails/compact_blank.rb +111 -0
  34. data/lib/rubocop/cop/rails/content_tag.rb +93 -0
  35. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +22 -15
  36. data/lib/rubocop/cop/rails/date.rb +41 -36
  37. data/lib/rubocop/cop/rails/default_scope.rb +61 -0
  38. data/lib/rubocop/cop/rails/delegate.rb +33 -29
  39. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +9 -10
  40. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +168 -0
  41. data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
  42. data/lib/rubocop/cop/rails/duplicate_association.rb +56 -0
  43. data/lib/rubocop/cop/rails/duplicate_scope.rb +46 -0
  44. data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
  45. data/lib/rubocop/cop/rails/dynamic_find_by.rb +76 -31
  46. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +82 -0
  47. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  48. data/lib/rubocop/cop/rails/enum_uniqueness.rb +30 -12
  49. data/lib/rubocop/cop/rails/environment_comparison.rb +70 -22
  50. data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
  51. data/lib/rubocop/cop/rails/exit.rb +7 -13
  52. data/lib/rubocop/cop/rails/expanded_date_range.rb +102 -0
  53. data/lib/rubocop/cop/rails/file_path.rb +48 -31
  54. data/lib/rubocop/cop/rails/find_by.rb +43 -24
  55. data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
  56. data/lib/rubocop/cop/rails/find_each.rb +42 -18
  57. data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
  58. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +4 -3
  59. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +62 -25
  60. data/lib/rubocop/cop/rails/helper_instance_variable.rb +32 -4
  61. data/lib/rubocop/cop/rails/http_positional_arguments.rb +61 -32
  62. data/lib/rubocop/cop/rails/http_status.rb +27 -23
  63. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +96 -0
  64. data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +37 -0
  65. data/lib/rubocop/cop/rails/i18n_locale_texts.rb +110 -0
  66. data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
  67. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +9 -16
  68. data/lib/rubocop/cop/rails/index_by.rb +65 -0
  69. data/lib/rubocop/cop/rails/index_with.rb +68 -0
  70. data/lib/rubocop/cop/rails/inquiry.rb +39 -0
  71. data/lib/rubocop/cop/rails/inverse_of.rb +33 -27
  72. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +62 -32
  73. data/lib/rubocop/cop/rails/link_to_blank.rb +31 -32
  74. data/lib/rubocop/cop/rails/mailer_name.rb +90 -0
  75. data/lib/rubocop/cop/rails/match_route.rb +120 -0
  76. data/lib/rubocop/cop/rails/migration_class_name.rb +63 -0
  77. data/lib/rubocop/cop/rails/negate_include.rb +42 -0
  78. data/lib/rubocop/cop/rails/not_null_column.rb +16 -12
  79. data/lib/rubocop/cop/rails/order_by_id.rb +51 -0
  80. data/lib/rubocop/cop/rails/output.rb +29 -10
  81. data/lib/rubocop/cop/rails/output_safety.rb +9 -4
  82. data/lib/rubocop/cop/rails/pick.rb +64 -0
  83. data/lib/rubocop/cop/rails/pluck.rb +96 -0
  84. data/lib/rubocop/cop/rails/pluck_id.rb +59 -0
  85. data/lib/rubocop/cop/rails/pluck_in_where.rb +71 -0
  86. data/lib/rubocop/cop/rails/pluralization_grammar.rb +14 -19
  87. data/lib/rubocop/cop/rails/presence.rb +54 -26
  88. data/lib/rubocop/cop/rails/present.rb +40 -37
  89. data/lib/rubocop/cop/rails/rake_environment.rb +112 -0
  90. data/lib/rubocop/cop/rails/read_write_attribute.rb +56 -18
  91. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +33 -45
  92. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +77 -0
  93. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
  94. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +34 -32
  95. data/lib/rubocop/cop/rails/redundant_travel_back.rb +57 -0
  96. data/lib/rubocop/cop/rails/reflection_class_name.rb +56 -7
  97. data/lib/rubocop/cop/rails/refute_methods.rb +56 -35
  98. data/lib/rubocop/cop/rails/relative_date_constant.rb +52 -33
  99. data/lib/rubocop/cop/rails/render_inline.rb +41 -0
  100. data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
  101. data/lib/rubocop/cop/rails/request_referer.rb +10 -11
  102. data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
  103. data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
  104. data/lib/rubocop/cop/rails/reversible_migration.rb +122 -82
  105. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +66 -0
  106. data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
  107. data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
  108. data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
  109. data/lib/rubocop/cop/rails/safe_navigation.rb +55 -43
  110. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +50 -0
  111. data/lib/rubocop/cop/rails/save_bang.rb +89 -63
  112. data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
  113. data/lib/rubocop/cop/rails/scope_args.rb +8 -3
  114. data/lib/rubocop/cop/rails/short_i18n.rb +71 -0
  115. data/lib/rubocop/cop/rails/skips_model_validations.rb +53 -16
  116. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +87 -0
  117. data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
  118. data/lib/rubocop/cop/rails/table_name_assignment.rb +44 -0
  119. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
  120. data/lib/rubocop/cop/rails/time_zone.rb +83 -67
  121. data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
  122. data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
  123. data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
  124. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  125. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +99 -0
  126. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +40 -49
  127. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +172 -0
  128. data/lib/rubocop/cop/rails/unknown_env.rb +52 -21
  129. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +76 -0
  130. data/lib/rubocop/cop/rails/validation.rb +54 -23
  131. data/lib/rubocop/cop/rails/where_equals.rb +102 -0
  132. data/lib/rubocop/cop/rails/where_exists.rb +138 -0
  133. data/lib/rubocop/cop/rails/where_missing.rb +118 -0
  134. data/lib/rubocop/cop/rails/where_not.rb +101 -0
  135. data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
  136. data/lib/rubocop/cop/rails_cops.rb +78 -8
  137. data/lib/rubocop/rails/inject.rb +1 -1
  138. data/lib/rubocop/rails/schema_loader/schema.rb +191 -0
  139. data/lib/rubocop/rails/schema_loader.rb +61 -0
  140. data/lib/rubocop/rails/version.rb +5 -1
  141. data/lib/rubocop/rails.rb +3 -1
  142. data/lib/rubocop-rails.rb +22 -0
  143. metadata +120 -19
  144. data/bin/setup +0 -7
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks if collection can be blank-compacted with `compact_blank`.
7
+ #
8
+ # @safety
9
+ # It is unsafe by default because false positives may occur in the
10
+ # blank check of block arguments to the receiver object.
11
+ #
12
+ # For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and
13
+ # `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `blank?`.
14
+ # This will work fine when the receiver is a hash object.
15
+ #
16
+ # And `compact_blank!` has different implementations for `Array`, `Hash`, and
17
+ # `ActionController::Parameters`.
18
+ # `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
19
+ # `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
20
+ # If the cop makes a mistake, autocorrected code may get unexpected behavior.
21
+ #
22
+ # @example
23
+ #
24
+ # # bad
25
+ # collection.reject(&:blank?)
26
+ # collection.reject { |_k, v| v.blank? }
27
+ #
28
+ # # good
29
+ # collection.compact_blank
30
+ #
31
+ # # bad
32
+ # collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
33
+ # collection.delete_if { |_k, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
34
+ # collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
35
+ # collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
36
+ #
37
+ # # good
38
+ # collection.compact_blank!
39
+ #
40
+ class CompactBlank < Base
41
+ include RangeHelp
42
+ extend AutoCorrector
43
+ extend TargetRailsVersion
44
+
45
+ MSG = 'Use `%<preferred_method>s` instead.'
46
+ RESTRICT_ON_SEND = %i[reject delete_if reject!].freeze
47
+
48
+ minimum_target_rails_version 6.1
49
+
50
+ def_node_matcher :reject_with_block?, <<~PATTERN
51
+ (block
52
+ (send _ {:reject :delete_if :reject!})
53
+ $(args ...)
54
+ (send
55
+ $(lvar _) :blank?))
56
+ PATTERN
57
+
58
+ def_node_matcher :reject_with_block_pass?, <<~PATTERN
59
+ (send _ {:reject :delete_if :reject!}
60
+ (block_pass
61
+ (sym :blank?)))
62
+ PATTERN
63
+
64
+ def on_send(node)
65
+ return unless bad_method?(node)
66
+
67
+ range = offense_range(node)
68
+ preferred_method = preferred_method(node)
69
+ add_offense(range, message: format(MSG, preferred_method: preferred_method)) do |corrector|
70
+ corrector.replace(range, preferred_method)
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def bad_method?(node)
77
+ return true if reject_with_block_pass?(node)
78
+
79
+ if (arguments, receiver_in_block = reject_with_block?(node.parent))
80
+ return use_single_value_block_argument?(arguments, receiver_in_block) ||
81
+ use_hash_value_block_argument?(arguments, receiver_in_block)
82
+ end
83
+
84
+ false
85
+ end
86
+
87
+ def use_single_value_block_argument?(arguments, receiver_in_block)
88
+ arguments.length == 1 && arguments[0].source == receiver_in_block.source
89
+ end
90
+
91
+ def use_hash_value_block_argument?(arguments, receiver_in_block)
92
+ arguments.length == 2 && arguments[1].source == receiver_in_block.source
93
+ end
94
+
95
+ def offense_range(node)
96
+ end_pos = if node.parent&.block_type? && node.parent&.send_node == node
97
+ node.parent.source_range.end_pos
98
+ else
99
+ node.source_range.end_pos
100
+ end
101
+
102
+ range_between(node.loc.selector.begin_pos, end_pos)
103
+ end
104
+
105
+ def preferred_method(node)
106
+ node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks legacy syntax usage of `tag`
7
+ #
8
+ # NOTE: Allow `tag` when the first argument is a variable because
9
+ # `tag(name)` is simpler rather than `tag.public_send(name)`.
10
+ # And this cop will be renamed to something like `LegacyTag` in the future. (e.g. RuboCop Rails 2.0)
11
+ #
12
+ # @example
13
+ # # bad
14
+ # tag(:p)
15
+ # tag(:br, class: 'classname')
16
+ #
17
+ # # good
18
+ # tag.p
19
+ # tag.br(class: 'classname')
20
+ # tag(name, class: 'classname')
21
+ class ContentTag < Base
22
+ include RangeHelp
23
+ extend AutoCorrector
24
+ extend TargetRailsVersion
25
+
26
+ minimum_target_rails_version 5.1
27
+
28
+ MSG = 'Use `tag.%<preferred_method>s` instead of `tag(%<current_argument>s)`.'
29
+ RESTRICT_ON_SEND = %i[tag].freeze
30
+
31
+ def on_new_investigation
32
+ @corrected_nodes = nil
33
+ end
34
+
35
+ def on_send(node)
36
+ return unless node.receiver.nil?
37
+ return if node.arguments.count >= 3
38
+
39
+ first_argument = node.first_argument
40
+ return if !first_argument || allowed_argument?(first_argument) || corrected_ancestor?(node)
41
+
42
+ preferred_method = node.first_argument.value.to_s.underscore
43
+ message = format(MSG, preferred_method: preferred_method, current_argument: first_argument.source)
44
+
45
+ register_offense(node, message, preferred_method)
46
+ end
47
+
48
+ private
49
+
50
+ def corrected_ancestor?(node)
51
+ node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
52
+ end
53
+
54
+ def allowed_argument?(argument)
55
+ argument.variable? ||
56
+ argument.send_type? ||
57
+ argument.const_type? ||
58
+ argument.splat_type? ||
59
+ allowed_name?(argument) ||
60
+ !argument.respond_to?(:value)
61
+ end
62
+
63
+ def register_offense(node, message, preferred_method)
64
+ add_offense(node, message: message) do |corrector|
65
+ autocorrect(corrector, node, preferred_method)
66
+
67
+ @corrected_nodes ||= Set.new.compare_by_identity
68
+ @corrected_nodes.add(node)
69
+ end
70
+ end
71
+
72
+ def autocorrect(corrector, node, preferred_method)
73
+ range = correction_range(node)
74
+
75
+ rest_args = node.arguments.drop(1)
76
+ replacement = "tag.#{preferred_method}(#{rest_args.map(&:source).join(', ')})"
77
+
78
+ corrector.replace(range, replacement)
79
+ end
80
+
81
+ def allowed_name?(argument)
82
+ return false unless argument.str_type? || argument.sym_type?
83
+
84
+ !/^[a-zA-Z-][a-zA-Z\-0-9]*$/.match?(argument.value)
85
+ end
86
+
87
+ def correction_range(node)
88
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -3,10 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks the migration for which timestamps are not included
7
- # when creating a new table.
6
+ # Checks the migration for which timestamps are not included when creating a new table.
8
7
  # In many cases, timestamps are useful information and should be added.
9
8
  #
9
+ # NOTE: Allow `timestamps` not written when `id: false` because this emphasizes respecting
10
+ # user's editing intentions.
11
+ #
10
12
  # @example
11
13
  # # bad
12
14
  # create_table :users
@@ -40,39 +42,44 @@ module RuboCop
40
42
  #
41
43
  # t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
42
44
  # end
43
- class CreateTableWithTimestamps < Cop
45
+ #
46
+ # # good
47
+ # create_table :users, articles, id: false do |t|
48
+ # t.integer :user_id
49
+ # t.integer :article_id
50
+ # end
51
+ #
52
+ class CreateTableWithTimestamps < Base
53
+ include ActiveRecordMigrationsHelper
54
+
44
55
  MSG = 'Add timestamps when creating a new table.'
56
+ RESTRICT_ON_SEND = %i[create_table].freeze
45
57
 
46
- def_node_matcher :create_table_with_block?, <<-PATTERN
47
- (block
48
- (send nil? :create_table ...)
49
- (args (arg _var))
50
- _)
58
+ def_node_search :use_id_false_option?, <<~PATTERN
59
+ (pair (sym :id) (false))
51
60
  PATTERN
52
61
 
53
- def_node_matcher :create_table_with_timestamps_proc?, <<-PATTERN
62
+ def_node_matcher :create_table_with_timestamps_proc?, <<~PATTERN
54
63
  (send nil? :create_table (sym _) ... (block-pass (sym :timestamps)))
55
64
  PATTERN
56
65
 
57
- def_node_search :timestamps_included?, <<-PATTERN
66
+ def_node_search :timestamps_included?, <<~PATTERN
58
67
  (send _var :timestamps ...)
59
68
  PATTERN
60
69
 
61
- def_node_search :created_at_or_updated_at_included?, <<-PATTERN
70
+ def_node_search :created_at_or_updated_at_included?, <<~PATTERN
62
71
  (send _var :datetime
63
72
  {(sym {:created_at :updated_at})(str {"created_at" "updated_at"})}
64
73
  ...)
65
74
  PATTERN
66
75
 
67
76
  def on_send(node)
68
- return unless node.command?(:create_table)
77
+ return if !node.command?(:create_table) || use_id_false_option?(node)
69
78
 
70
79
  parent = node.parent
71
80
 
72
81
  if create_table_with_block?(parent)
73
- if parent.body.nil? || !time_columns_included?(parent.body)
74
- add_offense(parent)
75
- end
82
+ add_offense(parent) if parent.body.nil? || !time_columns_included?(parent.body)
76
83
  elsif create_table_with_timestamps_proc?(node)
77
84
  # nothing to do
78
85
  else
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks for the correct use of Date methods,
6
+ # Checks for the correct use of Date methods,
7
7
  # such as Date.today, Date.current etc.
8
8
  #
9
9
  # Using `Date.today` is dangerous, because it doesn't know anything about
@@ -12,54 +12,58 @@ module RuboCop
12
12
  # The cop also reports warnings when you are using `to_time` method,
13
13
  # because it doesn't know about Rails time zone either.
14
14
  #
15
- # Two styles are supported for this cop. When EnforcedStyle is 'strict'
15
+ # Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
16
16
  # then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
17
17
  # are prohibited and the usage of both `to_time`
18
18
  # and 'to_time_in_current_zone' are reported as warning.
19
19
  #
20
- # When EnforcedStyle is 'flexible' then only `Date.today` is prohibited
21
- # and only `to_time` is reported as warning.
20
+ # When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
22
21
  #
23
- # @example EnforcedStyle: strict
22
+ # And you can set a warning for `to_time` with `AllowToTime: false`.
23
+ # `AllowToTime` is `true` by default to prevent false positive on `DateTime` object.
24
+ #
25
+ # @example EnforcedStyle: flexible (default)
24
26
  # # bad
25
- # Date.current
26
- # Date.yesterday
27
27
  # Date.today
28
- # date.to_time
29
28
  #
30
29
  # # good
31
30
  # Time.zone.today
32
31
  # Time.zone.today - 1.day
32
+ # Date.current
33
+ # Date.yesterday
34
+ # date.in_time_zone
33
35
  #
34
- # @example EnforcedStyle: flexible (default)
36
+ # @example EnforcedStyle: strict
35
37
  # # bad
38
+ # Date.current
39
+ # Date.yesterday
36
40
  # Date.today
37
- # date.to_time
38
41
  #
39
42
  # # good
40
43
  # Time.zone.today
41
44
  # Time.zone.today - 1.day
42
- # Date.current
43
- # Date.yesterday
44
- # date.in_time_zone
45
45
  #
46
- class Date < Cop
46
+ # @example AllowToTime: true (default)
47
+ # # good
48
+ # date.to_time
49
+ #
50
+ # @example AllowToTime: false
51
+ # # bad
52
+ # date.to_time
53
+ class Date < Base
47
54
  include ConfigurableEnforcedStyle
48
55
 
49
- MSG = 'Do not use `Date.%<method_called>s` without zone. Use ' \
50
- '`Time.zone.%<day>s` instead.'
56
+ MSG = 'Do not use `Date.%<method_called>s` without zone. Use `Time.zone.%<day>s` instead.'
57
+
58
+ MSG_SEND = 'Do not use `%<method>s` on Date objects, because they know nothing about the time zone in use.'
51
59
 
52
- MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
53
- 'know nothing about the time zone in use.'
60
+ RESTRICT_ON_SEND = %i[to_time to_time_in_current_zone].freeze
54
61
 
55
62
  BAD_DAYS = %i[today current yesterday tomorrow].freeze
56
63
 
57
- DEPRECATED_METHODS = [
58
- { deprecated: 'to_time_in_current_zone', relevant: 'in_time_zone' }
59
- ].freeze
64
+ DEPRECATED_METHODS = [{ deprecated: 'to_time_in_current_zone', relevant: 'in_time_zone' }].freeze
60
65
 
61
- DEPRECATED_MSG = '`%<deprecated>s` is deprecated. ' \
62
- 'Use `%<relevant>s` instead.'
66
+ DEPRECATED_MSG = '`%<deprecated>s` is deprecated. Use `%<relevant>s` instead.'
63
67
 
64
68
  def on_const(node)
65
69
  mod, klass = *node.children
@@ -71,26 +75,24 @@ module RuboCop
71
75
 
72
76
  def on_send(node)
73
77
  return unless node.receiver && bad_methods.include?(node.method_name)
74
-
78
+ return if allow_to_time? && node.method?(:to_time)
75
79
  return if safe_chain?(node) || safe_to_time?(node)
76
80
 
77
81
  check_deprecated_methods(node)
78
82
 
79
- add_offense(node, location: :selector,
80
- message: format(MSG_SEND, method: node.method_name))
83
+ add_offense(node.loc.selector, message: format(MSG_SEND, method: node.method_name))
81
84
  end
82
85
  alias on_csend on_send
83
86
 
84
87
  private
85
88
 
86
89
  def check_deprecated_methods(node)
87
- DEPRECATED_METHODS.each do |relevant:, deprecated:|
88
- next unless node.method_name == deprecated.to_sym
90
+ DEPRECATED_METHODS.each do |method|
91
+ next unless node.method?(method[:deprecated].to_sym)
92
+
93
+ message = format(DEPRECATED_MSG, deprecated: method[:deprecated], relevant: method[:relevant])
89
94
 
90
- add_offense(node, location: :selector,
91
- message: format(DEPRECATED_MSG,
92
- deprecated: deprecated,
93
- relevant: relevant))
95
+ add_offense(node.loc.selector, message: message)
94
96
  end
95
97
  end
96
98
 
@@ -104,10 +106,9 @@ module RuboCop
104
106
  day = method_name
105
107
  day = 'today' if method_name == 'current'
106
108
 
107
- add_offense(node, location: :selector,
108
- message: format(MSG,
109
- method_called: method_name,
110
- day: day))
109
+ message = format(MSG, method_called: method_name, day: day)
110
+
111
+ add_offense(node.loc.selector, message: message)
111
112
  end
112
113
 
113
114
  def extract_method_chain(node)
@@ -140,6 +141,10 @@ module RuboCop
140
141
  end
141
142
  end
142
143
 
144
+ def allow_to_time?
145
+ cop_config.fetch('AllowToTime', true)
146
+ end
147
+
143
148
  def good_days
144
149
  style == :strict ? [] : %i[current yesterday tomorrow]
145
150
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Looks for uses of `default_scope`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # default_scope -> { where(hidden: false) }
11
+ #
12
+ # # good
13
+ # scope :published, -> { where(hidden: false) }
14
+ #
15
+ # # bad
16
+ # def self.default_scope
17
+ # where(hidden: false)
18
+ # end
19
+ #
20
+ # # good
21
+ # def self.published
22
+ # where(hidden: false)
23
+ # end
24
+ #
25
+ class DefaultScope < Base
26
+ MSG = 'Avoid use of `default_scope`. It is better to use explicitly named scopes.'
27
+ RESTRICT_ON_SEND = %i[default_scope].freeze
28
+
29
+ def_node_matcher :method_call?, <<~PATTERN
30
+ (send nil? :default_scope ...)
31
+ PATTERN
32
+
33
+ def_node_matcher :class_method_definition?, <<~PATTERN
34
+ (defs _ :default_scope args ...)
35
+ PATTERN
36
+
37
+ def_node_matcher :eigenclass_method_definition?, <<~PATTERN
38
+ (sclass (self) $(def :default_scope args ...))
39
+ PATTERN
40
+
41
+ def on_send(node)
42
+ return unless method_call?(node)
43
+
44
+ add_offense(node.loc.selector)
45
+ end
46
+
47
+ def on_defs(node)
48
+ return unless class_method_definition?(node)
49
+
50
+ add_offense(node.loc.name)
51
+ end
52
+
53
+ def on_sclass(node)
54
+ eigenclass_method_definition?(node) do |default_scope|
55
+ add_offense(default_scope.loc.name)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for delegations that could have been created
6
+ # Looks for delegations that could have been created
7
7
  # automatically with the `delegate` method.
8
8
  #
9
9
  # Safe navigation `&.` is ignored because Rails' `allow_nil`
@@ -24,6 +24,14 @@ module RuboCop
24
24
  # # good
25
25
  # delegate :bar, to: :foo
26
26
  #
27
+ # # bad
28
+ # def bar
29
+ # self.bar
30
+ # end
31
+ #
32
+ # # good
33
+ # delegate :bar, to: :self
34
+ #
27
35
  # # good
28
36
  # def bar
29
37
  # foo&.bar
@@ -52,36 +60,39 @@ module RuboCop
52
60
  #
53
61
  # # good
54
62
  # delegate :bar, to: :foo, prefix: true
55
- class Delegate < Cop
63
+ class Delegate < Base
64
+ extend AutoCorrector
65
+ include VisibilityHelp
66
+
56
67
  MSG = 'Use `delegate` to define delegations.'
57
68
 
58
- def_node_matcher :delegate?, <<-PATTERN
69
+ def_node_matcher :delegate?, <<~PATTERN
59
70
  (def _method_name _args
60
- (send (send nil? _) _ ...))
71
+ (send {(send nil? _) (self)} _ ...))
61
72
  PATTERN
62
73
 
63
74
  def on_def(node)
64
75
  return unless trivial_delegate?(node)
65
76
  return if private_or_protected_delegation(node)
66
77
 
67
- add_offense(node, location: :keyword)
78
+ register_offense(node)
68
79
  end
69
80
 
70
- def autocorrect(node)
71
- delegation = ["delegate :#{node.body.method_name}",
72
- "to: :#{node.body.receiver.method_name}"]
81
+ private
82
+
83
+ def register_offense(node)
84
+ add_offense(node.loc.keyword) do |corrector|
85
+ body = node.body
73
86
 
74
- if node.method_name == prefixed_method_name(node.body)
75
- delegation << ['prefix: true']
76
- end
87
+ receiver = body.receiver.self_type? ? 'self' : ":#{body.receiver.method_name}"
77
88
 
78
- lambda do |corrector|
79
- corrector.replace(node.source_range, delegation.join(', '))
89
+ delegation = ["delegate :#{body.method_name}", "to: #{receiver}"]
90
+ delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
91
+
92
+ corrector.replace(node, delegation.join(', '))
80
93
  end
81
94
  end
82
95
 
83
- private
84
-
85
96
  def trivial_delegate?(def_node)
86
97
  delegate?(def_node) &&
87
98
  method_name_matches?(def_node.method_name, def_node.body) &&
@@ -94,15 +105,12 @@ module RuboCop
94
105
  return false if arg_array.size != argument_array.size
95
106
 
96
107
  arg_array.zip(argument_array).all? do |arg, argument|
97
- arg.arg_type? &&
98
- argument.lvar_type? &&
99
- arg.children == argument.children
108
+ arg.arg_type? && argument.lvar_type? && arg.children == argument.children
100
109
  end
101
110
  end
102
111
 
103
112
  def method_name_matches?(method_name, body)
104
- method_name == body.method_name ||
105
- include_prefix_case? && method_name == prefixed_method_name(body)
113
+ method_name == body.method_name || (include_prefix_case? && method_name == prefixed_method_name(body))
106
114
  end
107
115
 
108
116
  def include_prefix_case?
@@ -110,21 +118,17 @@ module RuboCop
110
118
  end
111
119
 
112
120
  def prefixed_method_name(body)
121
+ return '' if body.receiver.self_type?
122
+
113
123
  [body.receiver.method_name, body.method_name].join('_').to_sym
114
124
  end
115
125
 
116
126
  def private_or_protected_delegation(node)
117
- line = node.first_line
118
- private_or_protected_before(line) ||
119
- private_or_protected_inline(line)
120
- end
121
-
122
- def private_or_protected_before(line)
123
- (processed_source[0..line].map(&:strip) & %w[private protected]).any?
127
+ private_or_protected_inline(node) || node_visibility(node) != :public
124
128
  end
125
129
 
126
- def private_or_protected_inline(line)
127
- processed_source[line - 1].strip =~ /\A(private )|(protected )/
130
+ def private_or_protected_inline(node)
131
+ processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
128
132
  end
129
133
  end
130
134
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop looks for delegations that pass :allow_blank as an option
6
+ # Looks for delegations that pass :allow_blank as an option
7
7
  # instead of :allow_nil. :allow_blank is not a valid option to pass
8
8
  # to ActiveSupport#delegate.
9
9
  #
@@ -13,22 +13,21 @@ module RuboCop
13
13
  #
14
14
  # # good
15
15
  # delegate :foo, to: :bar, allow_nil: true
16
- class DelegateAllowBlank < Cop
16
+ class DelegateAllowBlank < Base
17
+ extend AutoCorrector
18
+
17
19
  MSG = '`allow_blank` is not a valid option, use `allow_nil`.'
20
+ RESTRICT_ON_SEND = %i[delegate].freeze
18
21
 
19
- def_node_matcher :allow_blank_option, <<-PATTERN
22
+ def_node_matcher :allow_blank_option, <<~PATTERN
20
23
  (send nil? :delegate _ (hash <$(pair (sym :allow_blank) true) ...>))
21
24
  PATTERN
22
25
 
23
26
  def on_send(node)
24
- allow_blank_option(node) do |offending_node|
25
- add_offense(offending_node)
26
- end
27
- end
27
+ return unless (offending_node = allow_blank_option(node))
28
28
 
29
- def autocorrect(pair_node)
30
- lambda do |corrector|
31
- corrector.replace(pair_node.key.source_range, 'allow_nil')
29
+ add_offense(offending_node) do |corrector|
30
+ corrector.replace(offending_node.key, 'allow_nil')
32
31
  end
33
32
  end
34
33
  end