rubocop-rails 2.12.1 → 2.13.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +43 -8
  3. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +34 -0
  4. data/lib/rubocop/cop/rails/active_record_aliases.rb +6 -2
  5. data/lib/rubocop/cop/rails/application_controller.rb +5 -1
  6. data/lib/rubocop/cop/rails/application_job.rb +5 -1
  7. data/lib/rubocop/cop/rails/application_mailer.rb +5 -1
  8. data/lib/rubocop/cop/rails/application_record.rb +6 -1
  9. data/lib/rubocop/cop/rails/arel_star.rb +6 -0
  10. data/lib/rubocop/cop/rails/blank.rb +5 -4
  11. data/lib/rubocop/cop/rails/compact_blank.rb +98 -0
  12. data/lib/rubocop/cop/rails/content_tag.rb +15 -8
  13. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -7
  14. data/lib/rubocop/cop/rails/duration_arithmetic.rb +97 -0
  15. data/lib/rubocop/cop/rails/dynamic_find_by.rb +4 -0
  16. data/lib/rubocop/cop/rails/find_each.rb +13 -0
  17. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  18. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  19. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +8 -7
  20. data/lib/rubocop/cop/rails/mailer_name.rb +4 -0
  21. data/lib/rubocop/cop/rails/negate_include.rb +3 -2
  22. data/lib/rubocop/cop/rails/output.rb +4 -0
  23. data/lib/rubocop/cop/rails/pick.rb +7 -0
  24. data/lib/rubocop/cop/rails/pluck_id.rb +3 -0
  25. data/lib/rubocop/cop/rails/pluck_in_where.rb +7 -6
  26. data/lib/rubocop/cop/rails/rake_environment.rb +5 -0
  27. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +192 -0
  28. data/lib/rubocop/cop/rails/reflection_class_name.rb +4 -2
  29. data/lib/rubocop/cop/rails/relative_date_constant.rb +4 -1
  30. data/lib/rubocop/cop/rails/reversible_migration.rb +11 -3
  31. data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
  32. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +12 -3
  33. data/lib/rubocop/cop/rails/save_bang.rb +19 -0
  34. data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
  35. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +4 -2
  36. data/lib/rubocop/cop/rails/time_zone.rb +3 -0
  37. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +29 -35
  38. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  39. data/lib/rubocop/cop/rails/where_equals.rb +4 -0
  40. data/lib/rubocop/cop/rails/where_exists.rb +9 -8
  41. data/lib/rubocop/cop/rails_cops.rb +6 -0
  42. data/lib/rubocop/rails/version.rb +1 -1
  43. metadata +11 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fb49b448d769c9ff936129480584da1cfb68abc6f9697a845c94c7d345c7077
4
- data.tar.gz: 9309aca423686c3df184c22a25320f90f9eae5b0c2c033447fff1dbbc7458518
3
+ metadata.gz: f12ec262c68bd72f514b80f2140b51278a947f21726ef6307e6267eaec7df4ae
4
+ data.tar.gz: 2a09b4db338465d1904a19e8d4af871ba485e75244613299fabe473854125e4c
5
5
  SHA512:
6
- metadata.gz: 4d209b60d9c4cb2a1d1e0786936cb2ecac1d0debfc7fc83fc91e959a07a42a8f7ad8d5eba05c015b4c27cd79c342ce4e2f057399cbbd12af01218ff79f13d5b5
7
- data.tar.gz: a18599aa1218bedcd1854db939fad42265d1de222b0d5fc633f30844dc3f4bc65b97fc943511dc902c5bd8588fcbb20c3424d4db3b6d9ad4e534691a2d4cfe89
6
+ metadata.gz: fbf73dc2e978bc210e2dc190b8042720f24e6e374962b2873e6c29ec989ed2a7c1b05646c22a38d57f28e753104ac40a0e592730e63703d99dbb80fc6b498651
7
+ data.tar.gz: b70e78355a3fa5b09f3a2a1603c71b16547c3f2f80c0ce7c2dbbc3c38cae0a0ca9464a50895eb54ac8ccd558c9259bf28a1e4ebebb9e3d4f05102832f178fc11
data/config/default.yml CHANGED
@@ -9,7 +9,7 @@ AllCops:
9
9
  - bin/*
10
10
  - db/schema.rb
11
11
  # What version of Rails is the inspected code using? If a value is specified
12
- # for TargetRailsVersion then it is used. Acceptable values are specificed
12
+ # for TargetRailsVersion then it is used. Acceptable values are specified
13
13
  # as a float (i.e. 5.1); the patch version of Rails should not be included.
14
14
  # If TargetRailsVersion is not set, RuboCop will parse the Gemfile.lock or
15
15
  # gems.locked file to find the version of Rails that has been bound to the
@@ -180,6 +180,12 @@ Rails/BulkChangeTable:
180
180
  Include:
181
181
  - db/migrate/*.rb
182
182
 
183
+ Rails/CompactBlank:
184
+ Description: 'Checks if collection can be blank-compacted with `compact_blank`.'
185
+ Enabled: pending
186
+ Safe: false
187
+ VersionAdded: '2.13'
188
+
183
189
  Rails/ContentTag:
184
190
  Description: 'Use `tag.something` instead of `tag(:something)`.'
185
191
  Reference:
@@ -189,6 +195,9 @@ Rails/ContentTag:
189
195
  Enabled: true
190
196
  VersionAdded: '2.6'
191
197
  VersionChanged: '2.12'
198
+ # This `Exclude` config prevents false positives for `tag` calls to `has_one: tag`. No helpers are used in normal models.
199
+ Exclude:
200
+ - app/models/**/*.rb
192
201
 
193
202
  Rails/CreateTableWithTimestamps:
194
203
  Description: >-
@@ -198,6 +207,10 @@ Rails/CreateTableWithTimestamps:
198
207
  VersionAdded: '0.52'
199
208
  Include:
200
209
  - db/migrate/*.rb
210
+ Exclude:
211
+ # Respect the `active_storage_variant_records` table of `*_create_active_storage_tables.active_storage.rb`
212
+ # auto-generated by `bin/rails active_storage:install` even if `created_at` is not specified.
213
+ - db/migrate/*_create_active_storage_tables.active_storage.rb
201
214
 
202
215
  Rails/Date:
203
216
  Description: >-
@@ -238,6 +251,12 @@ Rails/DelegateAllowBlank:
238
251
  Enabled: true
239
252
  VersionAdded: '0.44'
240
253
 
254
+ Rails/DurationArithmetic:
255
+ Description: 'Do not use duration as arithmetic operand with `Time.current`.'
256
+ StyleGuide: 'https://rails.rubystyle.guide#duration-arithmetic'
257
+ Enabled: pending
258
+ VersionAdded: '2.13'
259
+
241
260
  Rails/DynamicFindBy:
242
261
  Description: 'Use `find_by` instead of dynamic `find_by_*`.'
243
262
  StyleGuide: 'https://rails.rubystyle.guide#find_by'
@@ -605,6 +624,11 @@ Rails/RedundantForeignKey:
605
624
  Enabled: true
606
625
  VersionAdded: '2.6'
607
626
 
627
+ Rails/RedundantPresenceValidationOnBelongsTo:
628
+ Description: 'Checks for redundant presence validation on belongs_to association.'
629
+ Enabled: pending
630
+ VersionAdded: '2.13'
631
+
608
632
  Rails/RedundantReceiverInWithOptions:
609
633
  Description: 'Checks for redundant receiver in `with_options`.'
610
634
  Enabled: true
@@ -639,9 +663,9 @@ Rails/RefuteMethods:
639
663
  Rails/RelativeDateConstant:
640
664
  Description: 'Do not assign relative date to constants.'
641
665
  Enabled: true
666
+ SafeAutoCorrect: false
642
667
  VersionAdded: '0.48'
643
- VersionChanged: '0.59'
644
- AutoCorrect: false
668
+ VersionChanged: '2.13'
645
669
 
646
670
  Rails/RenderInline:
647
671
  Description: 'Prefer using a template over inline rendering.'
@@ -688,6 +712,11 @@ Rails/ReversibleMigrationMethodDefinition:
688
712
  Include:
689
713
  - db/migrate/*.rb
690
714
 
715
+ Rails/RootJoinChain:
716
+ Description: 'Use a single `#join` instead of chaining on `Rails.root` or `Rails.public_path`.'
717
+ Enabled: pending
718
+ VersionAdded: '2.13'
719
+
691
720
  Rails/SafeNavigation:
692
721
  Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`."
693
722
  Enabled: true
@@ -720,6 +749,13 @@ Rails/SaveBang:
720
749
  AllowedReceivers: []
721
750
  SafeAutoCorrect: false
722
751
 
752
+ Rails/SchemaComment:
753
+ Description: >-
754
+ This cop enforces the use of the `comment` option when adding a new table or column
755
+ to the database during a migration.
756
+ Enabled: false
757
+ VersionAdded: '2.13'
758
+
723
759
  Rails/ScopeArgs:
724
760
  Description: 'Checks the arguments of ActiveRecord scopes.'
725
761
  Enabled: true
@@ -782,9 +818,9 @@ Rails/TimeZone:
782
818
  StyleGuide: 'https://rails.rubystyle.guide#time'
783
819
  Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
784
820
  Enabled: true
785
- Safe: false
821
+ SafeAutoCorrect: false
786
822
  VersionAdded: '0.30'
787
- VersionChanged: '2.10'
823
+ VersionChanged: '2.13'
788
824
  # The value `strict` means that `Time` should be used with `zone`.
789
825
  # The value `flexible` allows usage of `in_time_zone` instead of `zone`.
790
826
  EnforcedStyle: flexible
@@ -807,16 +843,15 @@ Rails/UniqBeforePluck:
807
843
  Description: 'Prefer the use of uniq or distinct before pluck.'
808
844
  Enabled: true
809
845
  VersionAdded: '0.40'
810
- VersionChanged: '2.8'
846
+ VersionChanged: '2.13'
811
847
  EnforcedStyle: conservative
812
848
  SupportedStyles:
813
849
  - conservative
814
850
  - aggressive
815
851
  SafeAutoCorrect: false
816
- AutoCorrect: false
817
852
 
818
853
  Rails/UniqueValidationWithoutIndex:
819
- Description: 'Uniqueness validation should be with a unique index.'
854
+ Description: 'Uniqueness validation should have a unique index on the database column.'
820
855
  Enabled: true
821
856
  VersionAdded: '2.5'
822
857
  Include:
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to extend cops for Active Record features
6
+ module ActiveRecordMigrationsHelper
7
+ extend NodePattern::Macros
8
+
9
+ RAILS_ABSTRACT_SCHEMA_DEFINITIONS = %i[
10
+ bigint binary boolean date datetime decimal float integer json string
11
+ text time timestamp virtual
12
+ ].freeze
13
+ RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS = %i[
14
+ column references belongs_to primary_key numeric
15
+ ].freeze
16
+ POSTGRES_SCHEMA_DEFINITIONS = %i[
17
+ bigserial bit bit_varying cidr citext daterange hstore inet interval
18
+ int4range int8range jsonb ltree macaddr money numrange oid point line
19
+ lseg box path polygon circle serial tsrange tstzrange tsvector uuid xml
20
+ ].freeze
21
+ MYSQL_SCHEMA_DEFINITIONS = %i[
22
+ blob tinyblob mediumblob longblob tinytext mediumtext longtext
23
+ unsigned_integer unsigned_bigint unsigned_float unsigned_decimal
24
+ ].freeze
25
+
26
+ def_node_matcher :create_table_with_block?, <<~PATTERN
27
+ (block
28
+ (send nil? :create_table ...)
29
+ (args (arg _var))
30
+ _)
31
+ PATTERN
32
+ end
33
+ end
34
+ end
@@ -6,12 +6,16 @@ module RuboCop
6
6
  # Checks that ActiveRecord aliases are not used. The direct method names
7
7
  # are more clear and easier to read.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because custom `update_attributes` method call was changed to
11
+ # `update` but the method name remained same in the method definition.
12
+ #
9
13
  # @example
10
14
  # #bad
11
- # Book.update_attributes!(author: 'Alice')
15
+ # book.update_attributes!(author: 'Alice')
12
16
  #
13
17
  # #good
14
- # Book.update!(author: 'Alice')
18
+ # book.update!(author: 'Alice')
15
19
  class ActiveRecordAliases < Base
16
20
  extend AutoCorrector
17
21
 
@@ -3,7 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that controllers subclass ApplicationController.
6
+ # This cop checks that controllers subclass `ApplicationController`.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it may let the logic from `ApplicationController`
10
+ # sneak into a controller that is not purposed to inherit logic common among other controllers.
7
11
  #
8
12
  # @example
9
13
  #
@@ -3,7 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that jobs subclass ApplicationJob with Rails 5.0.
6
+ # This cop checks that jobs subclass `ApplicationJob` with Rails 5.0.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it may let the logic from `ApplicationJob`
10
+ # sneak into a job that is not purposed to inherit logic common among other jobs.
7
11
  #
8
12
  # @example
9
13
  #
@@ -3,7 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that mailers subclass ApplicationMailer with Rails 5.0.
6
+ # This cop checks that mailers subclass `ApplicationMailer` with Rails 5.0.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it may let the logic from `ApplicationMailer`
10
+ # sneak into a mailer that is not purposed to inherit logic common among other mailers.
7
11
  #
8
12
  # @example
9
13
  #
@@ -3,7 +3,12 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # This cop checks that models subclass ApplicationRecord with Rails 5.0.
6
+ # This cop checks that models subclass `ApplicationRecord` with Rails 5.0.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it may let the logic from `ApplicationRecord`
10
+ # sneak into an Active Record model that is not purposed to inherit logic common among other
11
+ # Active Record models.
7
12
  #
8
13
  # @example
9
14
  #
@@ -10,6 +10,12 @@ module RuboCop
10
10
  # database to look for a column named <tt>`*`</tt> (or `"*"`) as opposed
11
11
  # to expanding the column list as one would likely expect.
12
12
  #
13
+ # @safety
14
+ # This cop's autocorrection is unsafe because it turns a quoted `*` into
15
+ # an SQL `*`, unquoted. `*` is a valid column name in certain databases
16
+ # supported by Rails, and even though it is usually a mistake,
17
+ # it might denote legitimate access to a column named `*`.
18
+ #
13
19
  # @example
14
20
  # # bad
15
21
  # MyTable.arel_table["*"]
@@ -6,15 +6,16 @@ module RuboCop
6
6
  # This cop checks for code that can be written with simpler conditionals
7
7
  # using `Object#blank?` defined by Active Support.
8
8
  #
9
- # This cop is marked as unsafe auto-correction, because `' '.empty?` returns false,
10
- # but `' '.blank?` returns true. Therefore, auto-correction is not compatible
11
- # if the receiver is a non-empty blank string, tab, or newline meta characters.
12
- #
13
9
  # Interaction with `Style/UnlessElse`:
14
10
  # The configuration of `NotPresent` will not produce an offense in the
15
11
  # context of `unless else` if `Style/UnlessElse` is inabled. This is
16
12
  # to prevent interference between the auto-correction of the two cops.
17
13
  #
14
+ # @safety
15
+ # This cop is unsafe auto-correction, because `' '.empty?` returns false,
16
+ # but `' '.blank?` returns true. Therefore, auto-correction is not compatible
17
+ # if the receiver is a non-empty blank string, tab, or newline meta characters.
18
+ #
18
19
  # @example NilOrEmpty: true (default)
19
20
  # # Converts usages of `nil? || empty?` to `blank?`
20
21
  #
@@ -0,0 +1,98 @@
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 `empty?`.
14
+ # This will work fine when the receiver is a hash object.
15
+ #
16
+ # @example
17
+ #
18
+ # # bad
19
+ # collection.reject(&:blank?)
20
+ # collection.reject(&:empty?)
21
+ # collection.reject { |_k, v| v.blank? }
22
+ # collection.reject { |_k, v| v.empty? }
23
+ #
24
+ # # good
25
+ # collection.compact_blank
26
+ #
27
+ # # bad
28
+ # collection.reject!(&:blank?)
29
+ # collection.reject!(&:empty?)
30
+ # collection.reject! { |_k, v| v.blank? }
31
+ # collection.reject! { |_k, v| v.empty? }
32
+ #
33
+ # # good
34
+ # collection.compact_blank!
35
+ #
36
+ class CompactBlank < Base
37
+ include RangeHelp
38
+ extend AutoCorrector
39
+ extend TargetRailsVersion
40
+
41
+ MSG = 'Use `%<preferred_method>s` instead.'
42
+ RESTRICT_ON_SEND = %i[reject reject!].freeze
43
+
44
+ minimum_target_rails_version 6.1
45
+
46
+ def_node_matcher :reject_with_block?, <<~PATTERN
47
+ (block
48
+ (send _ {:reject :reject!})
49
+ $(args ...)
50
+ (send
51
+ $(lvar _) {:blank? :empty?}))
52
+ PATTERN
53
+
54
+ def_node_matcher :reject_with_block_pass?, <<~PATTERN
55
+ (send _ {:reject :reject!}
56
+ (block_pass
57
+ (sym {:blank? :empty?})))
58
+ PATTERN
59
+
60
+ def on_send(node)
61
+ return unless bad_method?(node)
62
+
63
+ range = offense_range(node)
64
+ preferred_method = preferred_method(node)
65
+ add_offense(range, message: format(MSG, preferred_method: preferred_method)) do |corrector|
66
+ corrector.replace(range, preferred_method)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def bad_method?(node)
73
+ return true if reject_with_block_pass?(node)
74
+
75
+ if (arguments, receiver_in_block = reject_with_block?(node.parent))
76
+ return arguments.length == 1 || use_hash_value_block_argument?(arguments, receiver_in_block)
77
+ end
78
+
79
+ false
80
+ end
81
+
82
+ def use_hash_value_block_argument?(arguments, receiver_in_block)
83
+ arguments.length == 2 && arguments[1].source == receiver_in_block.source
84
+ end
85
+
86
+ def offense_range(node)
87
+ end_pos = node.parent&.block_type? ? node.parent.loc.expression.end_pos : node.loc.expression.end_pos
88
+
89
+ range_between(node.loc.selector.begin_pos, end_pos)
90
+ end
91
+
92
+ def preferred_method(node)
93
+ node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -6,8 +6,8 @@ module RuboCop
6
6
  # This cop checks legacy syntax usage of `tag`
7
7
  #
8
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)
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
11
  #
12
12
  # @example
13
13
  # # bad
@@ -33,6 +33,9 @@ module RuboCop
33
33
  end
34
34
 
35
35
  def on_send(node)
36
+ return unless node.receiver.nil?
37
+ return if node.arguments.count >= 3
38
+
36
39
  first_argument = node.first_argument
37
40
  return if !first_argument ||
38
41
  allowed_argument?(first_argument) ||
@@ -41,12 +44,7 @@ module RuboCop
41
44
  preferred_method = node.first_argument.value.to_s.underscore
42
45
  message = format(MSG, preferred_method: preferred_method, current_argument: first_argument.source)
43
46
 
44
- add_offense(node, message: message) do |corrector|
45
- autocorrect(corrector, node, preferred_method)
46
-
47
- @corrected_nodes ||= Set.new.compare_by_identity
48
- @corrected_nodes.add(node)
49
- end
47
+ register_offense(node, message, preferred_method)
50
48
  end
51
49
 
52
50
  private
@@ -63,6 +61,15 @@ module RuboCop
63
61
  allowed_name?(argument)
64
62
  end
65
63
 
64
+ def register_offense(node, message, preferred_method)
65
+ add_offense(node, message: message) do |corrector|
66
+ autocorrect(corrector, node, preferred_method)
67
+
68
+ @corrected_nodes ||= Set.new.compare_by_identity
69
+ @corrected_nodes.add(node)
70
+ end
71
+ end
72
+
66
73
  def autocorrect(corrector, node, preferred_method)
67
74
  range = correction_range(node)
68
75
 
@@ -41,16 +41,11 @@ module RuboCop
41
41
  # t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
42
42
  # end
43
43
  class CreateTableWithTimestamps < Base
44
+ include ActiveRecordMigrationsHelper
45
+
44
46
  MSG = 'Add timestamps when creating a new table.'
45
47
  RESTRICT_ON_SEND = %i[create_table].freeze
46
48
 
47
- def_node_matcher :create_table_with_block?, <<~PATTERN
48
- (block
49
- (send nil? :create_table ...)
50
- (args (arg _var))
51
- _)
52
- PATTERN
53
-
54
49
  def_node_matcher :create_table_with_timestamps_proc?, <<~PATTERN
55
50
  (send nil? :create_table (sym _) ... (block-pass (sym :timestamps)))
56
51
  PATTERN
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks if a duration is added to or subtracted from `Time.current`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # Time.current - 1.minute
11
+ # Time.current + 2.days
12
+ #
13
+ # # good - using relative would make it harder to express and read
14
+ # Date.yesterday + 3.days
15
+ # created_at - 1.minute
16
+ # 3.days - 1.hour
17
+ #
18
+ # # good
19
+ # 1.minute.ago
20
+ # 2.days.from_now
21
+ class DurationArithmetic < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Do not add or subtract duration.'
25
+
26
+ RESTRICT_ON_SEND = %i[+ -].freeze
27
+
28
+ DURATIONS = Set[:second, :seconds, :minute, :minutes, :hour, :hours,
29
+ :day, :days, :week, :weeks, :fortnight, :fortnights]
30
+
31
+ # @!method duration_arithmetic_argument?(node)
32
+ # Match duration subtraction or addition with current time.
33
+ #
34
+ # @example source that matches
35
+ # Time.current - 1.hour
36
+ #
37
+ # @example source that matches
38
+ # ::Time.zone.now + 1.hour
39
+ #
40
+ # @param node [RuboCop::AST::Node]
41
+ # @yield operator and duration
42
+ def_node_matcher :duration_arithmetic_argument?, <<~PATTERN
43
+ (send #time_current? ${ :+ :- } $#duration?)
44
+ PATTERN
45
+
46
+ # @!method duration?(node)
47
+ # Match a literal Duration
48
+ #
49
+ # @example source that matches
50
+ # 1.hour
51
+ #
52
+ # @example source that matches
53
+ # 9.5.weeks
54
+ #
55
+ # @param node [RuboCop::AST::Node]
56
+ # @return [Boolean] true if matches
57
+ def_node_matcher :duration?, '(send { int float (send nil _) } DURATIONS)'
58
+
59
+ # @!method time_current?(node)
60
+ # Match Time.current
61
+ #
62
+ # @example source that matches
63
+ # Time.current
64
+ #
65
+ # @example source that matches
66
+ # ::Time.zone.now
67
+ #
68
+ # @param node [RuboCop::AST::Node]
69
+ # @return [Boolean] true if matches
70
+ def_node_matcher :time_current?, <<~PATTERN
71
+ {
72
+ (send (const _ :Time) :current)
73
+ (send (send (const _ :Time) :zone) :now)
74
+ }
75
+ PATTERN
76
+
77
+ def on_send(node)
78
+ duration_arithmetic_argument?(node) do |*operation|
79
+ add_offense(node) do |corrector|
80
+ corrector.replace(node.source_range, corrected_source(*operation))
81
+ end
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def corrected_source(operator, duration)
88
+ if operator == :-
89
+ "#{duration.source}.ago"
90
+ else
91
+ "#{duration.source}.from_now"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # Use `find_by` instead of dynamic method.
8
8
  # See. https://rails.rubystyle.guide#find_by
9
9
  #
10
+ # @safety
11
+ # It is certainly unsafe when not configured properly, i.e. user-defined `find_by_xxx`
12
+ # method is not added to cop's `AllowedMethods`.
13
+ #
10
14
  # @example
11
15
  # # bad
12
16
  # User.find_by_name(name)
@@ -43,9 +43,22 @@ module RuboCop
43
43
  private
44
44
 
45
45
  def ignored?(node)
46
+ return true if active_model_error_where?(node.receiver)
47
+
46
48
  method_chain = node.each_node(:send).map(&:method_name)
49
+
47
50
  (cop_config['IgnoredMethods'].map(&:to_sym) & method_chain).any?
48
51
  end
52
+
53
+ def active_model_error_where?(node)
54
+ node.method?(:where) && active_model_error?(node.receiver)
55
+ end
56
+
57
+ def active_model_error?(node)
58
+ return false if node.nil?
59
+
60
+ node.send_type? && node.method?(:errors)
61
+ end
49
62
  end
50
63
  end
51
64
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
  PATTERN
46
46
 
47
47
  def_node_matcher :association_with_options?, <<~PATTERN
48
- (send nil? {:has_many :has_one} _ (hash $...))
48
+ (send nil? {:has_many :has_one} ... (hash $...))
49
49
  PATTERN
50
50
 
51
51
  def_node_matcher :dependent_option?, <<~PATTERN
@@ -67,7 +67,7 @@ module RuboCop
67
67
  private
68
68
 
69
69
  def in_routing_block?(node)
70
- !!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.send_node.method_name) }
70
+ !!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.method_name) }
71
71
  end
72
72
 
73
73
  def needs_conversion?(data)
@@ -6,13 +6,14 @@ module RuboCop
6
6
  # This cop checks that methods specified in the filter's `only` or
7
7
  # `except` options are defined within the same class or module.
8
8
  #
9
- # You can technically specify methods of superclass or methods added by
10
- # mixins on the filter, but these can confuse developers. If you specify
11
- # methods that are defined in other classes or modules, you should
12
- # define the filter in that class or module.
13
- #
14
- # If you rely on behaviour defined in the superclass actions, you must
15
- # remember to invoke `super` in the subclass actions.
9
+ # @safety
10
+ # You can technically specify methods of superclass or methods added by
11
+ # mixins on the filter, but these can confuse developers. If you specify
12
+ # methods that are defined in other classes or modules, you should
13
+ # define the filter in that class or module.
14
+ #
15
+ # If you rely on behaviour defined in the superclass actions, you must
16
+ # remember to invoke `super` in the subclass actions.
16
17
  #
17
18
  # @example
18
19
  # # bad
@@ -8,6 +8,10 @@ module RuboCop
8
8
  # Without the `Mailer` suffix it isn't immediately apparent what's a mailer
9
9
  # and which views are related to the mailer.
10
10
  #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because renaming a constant is
13
+ # always an unsafe operation.
14
+ #
11
15
  # @example
12
16
  # # bad
13
17
  # class User < ActionMailer::Base
@@ -6,8 +6,9 @@ module RuboCop
6
6
  # This cop enforces the use of `collection.exclude?(obj)`
7
7
  # over `!collection.include?(obj)`.
8
8
  #
9
- # It is marked as unsafe by default because false positive will occur for
10
- # a receiver object that do not have `exclude?` method. (e.g. `IPAddr`)
9
+ # @safety
10
+ # This cop is unsafe because false positive will occur for
11
+ # receiver objects that do not have an `exclude?` method. (e.g. `IPAddr`)
11
12
  #
12
13
  # @example
13
14
  # # bad
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Rails
6
6
  # This cop checks for the use of output calls like puts and print
7
7
  #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because depending on the Rails log level configuration,
10
+ # changing from `puts` to `Rails.logger.debug` could result in no output being shown.
11
+ #
8
12
  # @example
9
13
  # # bad
10
14
  # puts 'A debug message'