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,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Looks for enums written with array syntax.
7
+ #
8
+ # When using array syntax, adding an element in a
9
+ # position other than the last causes all previous
10
+ # definitions to shift. Explicitly specifying the
11
+ # value for each key prevents this from happening.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # enum status: [:active, :archived]
16
+ #
17
+ # # good
18
+ # enum status: { active: 0, archived: 1 }
19
+ #
20
+ class EnumHash < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
24
+ RESTRICT_ON_SEND = %i[enum].freeze
25
+
26
+ def_node_matcher :enum?, <<~PATTERN
27
+ (send nil? :enum (hash $...))
28
+ PATTERN
29
+
30
+ def_node_matcher :array_pair?, <<~PATTERN
31
+ (pair $_ $array)
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ enum?(node) do |pairs|
36
+ pairs.each do |pair|
37
+ key, array = array_pair?(pair)
38
+ next unless key
39
+
40
+ add_offense(array, message: format(MSG, enum: enum_name(key))) do |corrector|
41
+ hash = array.children.each_with_index.map do |elem, index|
42
+ "#{source(elem)} => #{index}"
43
+ end.join(', ')
44
+
45
+ corrector.replace(array, "{#{hash}}")
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def enum_name(key)
54
+ case key.type
55
+ when :sym, :str
56
+ key.value
57
+ else
58
+ key.source
59
+ end
60
+ end
61
+
62
+ def source(elem)
63
+ case elem.type
64
+ when :str
65
+ elem.value.dump
66
+ when :sym
67
+ elem.value.inspect
68
+ else
69
+ elem.source
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ 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
@@ -17,28 +17,46 @@ module RuboCop
17
17
  #
18
18
  # # good
19
19
  # enum status: [:active, :archived]
20
- class EnumUniqueness < Cop
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.'
24
+ RESTRICT_ON_SEND = %i[enum].freeze
25
25
 
26
- def_node_matcher :enum_declaration, <<-PATTERN
27
- (send nil? :enum (hash (pair (_ $_) ${array hash})))
26
+ def_node_matcher :enum?, <<~PATTERN
27
+ (send nil? :enum (hash $...))
28
+ PATTERN
29
+
30
+ def_node_matcher :enum_values, <<~PATTERN
31
+ (pair $_ ${array hash})
28
32
  PATTERN
29
33
 
30
34
  def on_send(node)
31
- enum_declaration(node) do |name, args|
32
- items = args.values
35
+ enum?(node) do |pairs|
36
+ pairs.each do |pair|
37
+ enum_values(pair) do |key, args|
38
+ items = args.values
33
39
 
34
- return unless duplicates?(items)
40
+ next unless duplicates?(items)
35
41
 
36
- consecutive_duplicates(items).each do |item|
37
- add_offense(item, message: format(MSG, value: item.source,
38
- enum: name))
42
+ consecutive_duplicates(items).each do |item|
43
+ add_offense(item, message: format(MSG, value: item.source, enum: enum_name(key)))
44
+ end
45
+ end
39
46
  end
40
47
  end
41
48
  end
49
+
50
+ private
51
+
52
+ def enum_name(key)
53
+ case key.type
54
+ when :sym, :str
55
+ key.value
56
+ else
57
+ key.source
58
+ end
59
+ end
42
60
  end
43
61
  end
44
62
  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
@@ -15,53 +15,101 @@ module RuboCop
15
15
  #
16
16
  # # good
17
17
  # Rails.env.production?
18
- class EnvironmentComparison < Cop
19
- MSG = "Favor `Rails.env.%<env>s?` over `Rails.env == '%<env>s'`."
18
+ class EnvironmentComparison < Base
19
+ extend AutoCorrector
20
20
 
21
- SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
22
- 'evaluate to `false`.'
21
+ MSG = 'Favor `%<bang>sRails.env.%<env>s?` over `%<source>s`.'
23
22
 
24
- def_node_matcher :environment_str_comparison?, <<-PATTERN
23
+ SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always evaluate to `false`.'
24
+
25
+ RESTRICT_ON_SEND = %i[== !=].freeze
26
+
27
+ def_node_matcher :comparing_str_env_with_rails_env_on_lhs?, <<~PATTERN
25
28
  (send
26
29
  (send (const {nil? cbase} :Rails) :env)
27
- :==
30
+ {:== :!=}
31
+ $str
32
+ )
33
+ PATTERN
34
+
35
+ def_node_matcher :comparing_str_env_with_rails_env_on_rhs?, <<~PATTERN
36
+ (send
28
37
  $str
38
+ {:== :!=}
39
+ (send (const {nil? cbase} :Rails) :env)
29
40
  )
30
41
  PATTERN
31
42
 
32
- def_node_matcher :environment_sym_comparison?, <<-PATTERN
43
+ def_node_matcher :comparing_sym_env_with_rails_env_on_lhs?, <<~PATTERN
33
44
  (send
34
45
  (send (const {nil? cbase} :Rails) :env)
35
- :==
46
+ {:== :!=}
36
47
  $sym
37
48
  )
38
49
  PATTERN
39
50
 
51
+ def_node_matcher :comparing_sym_env_with_rails_env_on_rhs?, <<~PATTERN
52
+ (send
53
+ $sym
54
+ {:== :!=}
55
+ (send (const {nil? cbase} :Rails) :env)
56
+ )
57
+ PATTERN
58
+
59
+ def_node_matcher :content, <<~PATTERN
60
+ ({str sym} $_)
61
+ PATTERN
62
+
40
63
  def on_send(node)
41
- environment_str_comparison?(node) do |env_node|
64
+ if (env_node = comparing_str_env_with_rails_env_on_lhs?(node) ||
65
+ comparing_str_env_with_rails_env_on_rhs?(node))
42
66
  env, = *env_node
43
- add_offense(node, message: format(MSG, env: env))
67
+ bang = node.method?(:!=) ? '!' : ''
68
+ message = format(MSG, bang: bang, env: env, source: node.source)
69
+
70
+ add_offense(node, message: message) do |corrector|
71
+ autocorrect(corrector, node)
72
+ end
44
73
  end
45
- environment_sym_comparison?(node) do |_|
46
- add_offense(node, message: SYM_MSG)
74
+
75
+ return unless comparing_sym_env_with_rails_env_on_lhs?(node) || comparing_sym_env_with_rails_env_on_rhs?(node)
76
+
77
+ add_offense(node, message: SYM_MSG) do |corrector|
78
+ autocorrect(corrector, node)
47
79
  end
48
80
  end
49
81
 
50
- def autocorrect(node)
51
- lambda do |corrector|
52
- corrector.replace(node.source_range, replacement(node))
82
+ private
83
+
84
+ def autocorrect(corrector, node)
85
+ replacement = build_predicate_method(node)
86
+
87
+ corrector.replace(node, replacement)
88
+ end
89
+
90
+ def build_predicate_method(node)
91
+ if rails_env_on_lhs?(node)
92
+ build_predicate_method_for_rails_env_on_lhs(node)
93
+ else
94
+ build_predicate_method_for_rails_env_on_rhs(node)
53
95
  end
54
96
  end
55
97
 
56
- private
98
+ def rails_env_on_lhs?(node)
99
+ comparing_str_env_with_rails_env_on_lhs?(node) || comparing_sym_env_with_rails_env_on_lhs?(node)
100
+ end
101
+
102
+ def build_predicate_method_for_rails_env_on_lhs(node)
103
+ bang = node.method?(:!=) ? '!' : ''
57
104
 
58
- def replacement(node)
59
- "#{node.receiver.source}.#{content(node.first_argument)}?"
105
+ "#{bang}#{node.receiver.source}.#{content(node.first_argument)}?"
60
106
  end
61
107
 
62
- def_node_matcher :content, <<-PATTERN
63
- ({str sym} $_)
64
- PATTERN
108
+ def build_predicate_method_for_rails_env_on_rhs(node)
109
+ bang = node.method?(:!=) ? '!' : ''
110
+
111
+ "#{bang}#{node.first_argument.source}.#{content(node.receiver)}?"
112
+ end
65
113
  end
66
114
  end
67
115
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Looks for direct access to environment variables through the
7
+ # `ENV` variable within the application code. This can lead to runtime
8
+ # errors due to misconfiguration that could have been discovered at boot
9
+ # time if the environment variables were loaded as part of initialization
10
+ # and copied into the application's configuration or secrets. The cop can
11
+ # be configured to allow either reads or writes if required.
12
+ #
13
+ # @example
14
+ # # good
15
+ # Rails.application.config.foo
16
+ # Rails.application.config.x.foo.bar
17
+ # Rails.application.secrets.foo
18
+ # Rails.application.config.foo = "bar"
19
+ #
20
+ # @example AllowReads: false (default)
21
+ # # bad
22
+ # ENV["FOO"]
23
+ # ENV.fetch("FOO")
24
+ #
25
+ # @example AllowReads: true
26
+ # # good
27
+ # ENV["FOO"]
28
+ # ENV.fetch("FOO")
29
+ #
30
+ # @example AllowWrites: false (default)
31
+ # # bad
32
+ # ENV["FOO"] = "bar"
33
+ #
34
+ # @example AllowWrites: true
35
+ # # good
36
+ # ENV["FOO"] = "bar"
37
+ class EnvironmentVariableAccess < Base
38
+ READ_MSG = 'Do not read from `ENV` directly post initialization.'
39
+ WRITE_MSG = 'Do not write to `ENV` directly post initialization.'
40
+
41
+ def on_const(node)
42
+ add_offense(node, message: READ_MSG) if env_read?(node) && !allow_reads?
43
+ add_offense(node, message: WRITE_MSG) if env_write?(node) && !allow_writes?
44
+ end
45
+
46
+ def_node_search :env_read?, <<~PATTERN
47
+ ^(send (const {cbase nil?} :ENV) !:[]= ...)
48
+ PATTERN
49
+
50
+ def_node_search :env_write?, <<~PATTERN
51
+ {^(indexasgn (const {cbase nil?} :ENV) ...)
52
+ ^(send (const {cbase nil?} :ENV) :[]= ...)}
53
+ PATTERN
54
+
55
+ private
56
+
57
+ def allow_reads?
58
+ cop_config['AllowReads'] == true
59
+ end
60
+
61
+ def allow_writes?
62
+ cop_config['AllowWrites'] == true
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -3,17 +3,17 @@
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
  #
10
10
  # There are two obvious cases where `exit` is particularly harmful:
11
11
  #
12
- # - Usage in library code for your application. Even though Rails will
12
+ # * Usage in library code for your application. Even though Rails will
13
13
  # rescue from a `SystemExit` and continue on, unit testing that library
14
14
  # code will result in specs exiting (potentially silently if `exit(0)`
15
15
  # is used.)
16
- # - Usage in application code outside of the web process could result in
16
+ # * Usage in application code outside of the web process could result in
17
17
  # the program exiting, which could result in the code failing to run and
18
18
  # do its job.
19
19
  #
@@ -23,27 +23,21 @@ module RuboCop
23
23
  #
24
24
  # # good
25
25
  # raise 'a bad error has happened'
26
- class Exit < Cop
26
+ class Exit < Base
27
27
  include ConfigurableEnforcedStyle
28
28
 
29
29
  MSG = 'Do not use `exit` in Rails applications.'
30
- TARGET_METHODS = %i[exit exit!].freeze
30
+ RESTRICT_ON_SEND = %i[exit exit!].freeze
31
31
  EXPLICIT_RECEIVERS = %i[Kernel Process].freeze
32
32
 
33
33
  def on_send(node)
34
- add_offense(node, location: :selector) if offending_node?(node)
34
+ add_offense(node.loc.selector) if offending_node?(node)
35
35
  end
36
36
 
37
37
  private
38
38
 
39
39
  def offending_node?(node)
40
- right_method_name?(node.method_name) &&
41
- right_argument_count?(node.arguments) &&
42
- right_receiver?(node.receiver)
43
- end
44
-
45
- def right_method_name?(method_name)
46
- TARGET_METHODS.include?(method_name)
40
+ right_argument_count?(node.arguments) && right_receiver?(node.receiver)
47
41
  end
48
42
 
49
43
  # More than 1 argument likely means it is a different
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for expanded date range. It only compatible `..` range is targeted.
7
+ # Incompatible `...` range is ignored.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # date.beginning_of_day..date.end_of_day
12
+ # date.beginning_of_week..date.end_of_week
13
+ # date.beginning_of_month..date.end_of_month
14
+ # date.beginning_of_quarter..date.end_of_quarter
15
+ # date.beginning_of_year..date.end_of_year
16
+ #
17
+ # # good
18
+ # date.all_day
19
+ # date.all_week
20
+ # date.all_month
21
+ # date.all_quarter
22
+ # date.all_year
23
+ #
24
+ class ExpandedDateRange < Base
25
+ extend AutoCorrector
26
+ extend TargetRailsVersion
27
+
28
+ MSG = 'Use `%<preferred_method>s` instead.'
29
+
30
+ minimum_target_rails_version 5.1
31
+
32
+ PREFERRED_METHODS = {
33
+ beginning_of_day: 'all_day',
34
+ beginning_of_week: 'all_week',
35
+ beginning_of_month: 'all_month',
36
+ beginning_of_quarter: 'all_quarter',
37
+ beginning_of_year: 'all_year'
38
+ }.freeze
39
+
40
+ MAPPED_DATE_RANGE_METHODS = {
41
+ beginning_of_day: :end_of_day,
42
+ beginning_of_week: :end_of_week,
43
+ beginning_of_month: :end_of_month,
44
+ beginning_of_quarter: :end_of_quarter,
45
+ beginning_of_year: :end_of_year
46
+ }.freeze
47
+
48
+ def on_irange(node)
49
+ begin_node = node.begin
50
+ end_node = node.end
51
+ return if allow?(begin_node, end_node)
52
+
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)
56
+
57
+ preferred_method << "(#{begin_node.first_argument.source})"
58
+ elsif any_arguments?(begin_node, end_node)
59
+ return
60
+ end
61
+
62
+ register_offense(node, preferred_method)
63
+ end
64
+
65
+ private
66
+
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
+ end
79
+
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
98
+ end
99
+ end
100
+ end
101
+ end
102
+ 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 (default)
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
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
- class FilePath < Cop
37
+ class FilePath < Base
29
38
  include ConfigurableEnforcedStyle
30
39
  include RangeHelp
31
40
 
32
- MSG_SLASHES = 'Please use `Rails.root.join(\'path/to\')` ' \
33
- 'instead.'
34
- MSG_ARGUMENTS = 'Please use `Rails.root.join(\'path\', \'to\')` ' \
35
- 'instead.'
41
+ MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')%<to_s>s`.'
42
+ MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')%<to_s>s`.'
43
+ RESTRICT_ON_SEND = %i[join].freeze
36
44
 
37
- def_node_matcher :file_join_nodes?, <<-PATTERN
38
- (send (const nil? :File) :join ...)
45
+ def_node_matcher :file_join_nodes?, <<~PATTERN
46
+ (send (const {nil? cbase} :File) :join ...)
39
47
  PATTERN
40
48
 
41
- def_node_search :rails_root_nodes?, <<-PATTERN
42
- (send (const nil? :Rails) :root)
49
+ def_node_search :rails_root_nodes?, <<~PATTERN
50
+ (send (const {nil? cbase} :Rails) :root)
43
51
  PATTERN
44
52
 
45
- def_node_matcher :rails_root_join_nodes?, <<-PATTERN
46
- (send (send (const nil? :Rails) :root) :join ...)
53
+ def_node_matcher :rails_root_join_nodes?, <<~PATTERN
54
+ (send #rails_root_nodes? :join ...)
47
55
  PATTERN
48
56
 
49
57
  def on_dstr(node)
50
58
  return unless rails_root_nodes?(node)
51
- return unless node.children.last.source.start_with?('.') ||
52
- node.children.last.source.include?(File::SEPARATOR)
59
+ return unless node.children.last.str_type?
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
- node.str_type? && node.source =~ %r{/}
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(node, location: 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(_node)
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