rubocop-rails 2.12.3 → 2.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/config/default.yml +40 -8
  4. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +34 -0
  5. data/lib/rubocop/cop/mixin/migrations_helper.rb +26 -0
  6. data/lib/rubocop/cop/rails/active_record_aliases.rb +6 -2
  7. data/lib/rubocop/cop/rails/application_controller.rb +5 -1
  8. data/lib/rubocop/cop/rails/application_job.rb +5 -1
  9. data/lib/rubocop/cop/rails/application_mailer.rb +5 -1
  10. data/lib/rubocop/cop/rails/application_record.rb +6 -1
  11. data/lib/rubocop/cop/rails/arel_star.rb +6 -0
  12. data/lib/rubocop/cop/rails/blank.rb +5 -4
  13. data/lib/rubocop/cop/rails/compact_blank.rb +99 -0
  14. data/lib/rubocop/cop/rails/content_tag.rb +2 -2
  15. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -7
  16. data/lib/rubocop/cop/rails/duration_arithmetic.rb +98 -0
  17. data/lib/rubocop/cop/rails/dynamic_find_by.rb +4 -0
  18. data/lib/rubocop/cop/rails/find_each.rb +2 -0
  19. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  20. data/lib/rubocop/cop/rails/index_by.rb +6 -6
  21. data/lib/rubocop/cop/rails/index_with.rb +6 -6
  22. data/lib/rubocop/cop/rails/inverse_of.rb +17 -1
  23. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +8 -7
  24. data/lib/rubocop/cop/rails/mailer_name.rb +4 -0
  25. data/lib/rubocop/cop/rails/negate_include.rb +3 -2
  26. data/lib/rubocop/cop/rails/output.rb +4 -0
  27. data/lib/rubocop/cop/rails/pick.rb +7 -0
  28. data/lib/rubocop/cop/rails/pluck_id.rb +3 -0
  29. data/lib/rubocop/cop/rails/pluck_in_where.rb +7 -6
  30. data/lib/rubocop/cop/rails/rake_environment.rb +5 -0
  31. data/lib/rubocop/cop/rails/read_write_attribute.rb +51 -14
  32. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +257 -0
  33. data/lib/rubocop/cop/rails/reflection_class_name.rb +4 -2
  34. data/lib/rubocop/cop/rails/relative_date_constant.rb +3 -0
  35. data/lib/rubocop/cop/rails/reversible_migration.rb +5 -3
  36. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +2 -10
  37. data/lib/rubocop/cop/rails/root_join_chain.rb +72 -0
  38. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +12 -3
  39. data/lib/rubocop/cop/rails/save_bang.rb +19 -0
  40. data/lib/rubocop/cop/rails/schema_comment.rb +104 -0
  41. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +4 -2
  42. data/lib/rubocop/cop/rails/time_zone.rb +3 -0
  43. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +29 -35
  44. data/lib/rubocop/cop/rails/unused_ignored_columns.rb +2 -0
  45. data/lib/rubocop/cop/rails/where_equals.rb +4 -0
  46. data/lib/rubocop/cop/rails/where_exists.rb +9 -8
  47. data/lib/rubocop/cop/rails_cops.rb +7 -0
  48. data/lib/rubocop/rails/version.rb +1 -1
  49. metadata +12 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6f12cb9a10c2b1cf1ffe2a4713ccfdd4d88885378bde05d3988d829bf73218d
4
- data.tar.gz: e60c60867234af9be31f066397aa23ddfedf8b167fa86269761717a81ad89f75
3
+ metadata.gz: 87ab88a596e8f8dbe2ef0ded9a93ae1ceba10572ad41fc6b4e1b0b684cf68110
4
+ data.tar.gz: 4bc8502424aa32efec36f06ca475b7a3918b160060f76aa65eba39bfa5a31947
5
5
  SHA512:
6
- metadata.gz: b002f9f18331d8f4fe34f69dd0226e2ae607e632360e370dec3b5f2df39e4c1d10426ca5b9a574f83d579a841e705cb8b2332b6a2956dc76f6b130d56f824b30
7
- data.tar.gz: 5e84d9ed3b6a8991fa8b905d38e30a6c26828d8b9a73b0db552485872b0a31bff17aab3b6109170a98950b132eb1929be16552b27143f56567daf4e2ef9e7d16
6
+ metadata.gz: 27016cd823ac6eb75ddb06f97c613fea178b29dc8cadac6289c803a9745ccbe2ffd7f7799c82b1a9ddd74f08ab162fcb50acf9a491a5841f1d09931eec8145fa
7
+ data.tar.gz: 95c826339433a0a87f296676216e62b493cdc4d5b67ac73c400659194d3a54e874554d0431c3cbea02316d4265cd870755320ed5f5fd6b42722fdd17feba041e
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-21 Bozhidar Batsov
1
+ Copyright (c) 2012-22 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/config/default.yml CHANGED
@@ -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:
@@ -245,6 +251,12 @@ Rails/DelegateAllowBlank:
245
251
  Enabled: true
246
252
  VersionAdded: '0.44'
247
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
+
248
260
  Rails/DynamicFindBy:
249
261
  Description: 'Use `find_by` instead of dynamic `find_by_*`.'
250
262
  StyleGuide: 'https://rails.rubystyle.guide#find_by'
@@ -441,6 +453,7 @@ Rails/InverseOf:
441
453
  Description: 'Checks for associations where the inverse cannot be determined automatically.'
442
454
  Enabled: true
443
455
  VersionAdded: '0.52'
456
+ IgnoreScopes: false
444
457
  Include:
445
458
  - app/models/**/*.rb
446
459
 
@@ -612,6 +625,12 @@ Rails/RedundantForeignKey:
612
625
  Enabled: true
613
626
  VersionAdded: '2.6'
614
627
 
628
+ Rails/RedundantPresenceValidationOnBelongsTo:
629
+ Description: 'Checks for redundant presence validation on belongs_to association.'
630
+ Enabled: pending
631
+ SafeAutoCorrect: false
632
+ VersionAdded: '2.13'
633
+
615
634
  Rails/RedundantReceiverInWithOptions:
616
635
  Description: 'Checks for redundant receiver in `with_options`.'
617
636
  Enabled: true
@@ -646,9 +665,9 @@ Rails/RefuteMethods:
646
665
  Rails/RelativeDateConstant:
647
666
  Description: 'Do not assign relative date to constants.'
648
667
  Enabled: true
668
+ SafeAutoCorrect: false
649
669
  VersionAdded: '0.48'
650
- VersionChanged: '0.59'
651
- AutoCorrect: false
670
+ VersionChanged: '2.13'
652
671
 
653
672
  Rails/RenderInline:
654
673
  Description: 'Prefer using a template over inline rendering.'
@@ -685,15 +704,22 @@ Rails/ReversibleMigration:
685
704
  Reference: 'https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html'
686
705
  Enabled: true
687
706
  VersionAdded: '0.47'
707
+ VersionChanged: '2.13'
688
708
  Include:
689
- - db/migrate/*.rb
709
+ - db/**/*.rb
690
710
 
691
711
  Rails/ReversibleMigrationMethodDefinition:
692
712
  Description: 'Checks whether the migration implements either a `change` method or both an `up` and a `down` method.'
693
713
  Enabled: false
694
714
  VersionAdded: '2.10'
715
+ VersionChanged: '2.13'
695
716
  Include:
696
- - db/migrate/*.rb
717
+ - db/**/*.rb
718
+
719
+ Rails/RootJoinChain:
720
+ Description: 'Use a single `#join` instead of chaining on `Rails.root` or `Rails.public_path`.'
721
+ Enabled: pending
722
+ VersionAdded: '2.13'
697
723
 
698
724
  Rails/SafeNavigation:
699
725
  Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`."
@@ -727,6 +753,13 @@ Rails/SaveBang:
727
753
  AllowedReceivers: []
728
754
  SafeAutoCorrect: false
729
755
 
756
+ Rails/SchemaComment:
757
+ Description: >-
758
+ This cop enforces the use of the `comment` option when adding a new table or column
759
+ to the database during a migration.
760
+ Enabled: false
761
+ VersionAdded: '2.13'
762
+
730
763
  Rails/ScopeArgs:
731
764
  Description: 'Checks the arguments of ActiveRecord scopes.'
732
765
  Enabled: true
@@ -789,9 +822,9 @@ Rails/TimeZone:
789
822
  StyleGuide: 'https://rails.rubystyle.guide#time'
790
823
  Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
791
824
  Enabled: true
792
- Safe: false
825
+ SafeAutoCorrect: false
793
826
  VersionAdded: '0.30'
794
- VersionChanged: '2.10'
827
+ VersionChanged: '2.13'
795
828
  # The value `strict` means that `Time` should be used with `zone`.
796
829
  # The value `flexible` allows usage of `in_time_zone` instead of `zone`.
797
830
  EnforcedStyle: flexible
@@ -814,13 +847,12 @@ Rails/UniqBeforePluck:
814
847
  Description: 'Prefer the use of uniq or distinct before pluck.'
815
848
  Enabled: true
816
849
  VersionAdded: '0.40'
817
- VersionChanged: '2.8'
850
+ VersionChanged: '2.13'
818
851
  EnforcedStyle: conservative
819
852
  SupportedStyles:
820
853
  - conservative
821
854
  - aggressive
822
855
  SafeAutoCorrect: false
823
- AutoCorrect: false
824
856
 
825
857
  Rails/UniqueValidationWithoutIndex:
826
858
  Description: 'Uniqueness validation should have a unique index on the database column.'
@@ -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
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops working with migrations
6
+ module MigrationsHelper
7
+ extend NodePattern::Macros
8
+
9
+ def_node_matcher :migration_class?, <<~PATTERN
10
+ (class
11
+ (const nil? _)
12
+ (send
13
+ (const (const {nil? cbase} :ActiveRecord) :Migration)
14
+ :[]
15
+ (float _))
16
+ _)
17
+ PATTERN
18
+
19
+ def in_migration?(node)
20
+ node.each_ancestor(:class).any? do |class_node|
21
+ migration_class?(class_node)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ 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,99 @@
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
+ # @example
17
+ #
18
+ # # bad
19
+ # collection.reject(&:blank?)
20
+ # collection.reject { |_k, v| v.blank? }
21
+ #
22
+ # # good
23
+ # collection.compact_blank
24
+ #
25
+ # # bad
26
+ # collection.reject!(&:blank?)
27
+ # collection.reject! { |_k, v| v.blank? }
28
+ #
29
+ # # good
30
+ # collection.compact_blank!
31
+ #
32
+ class CompactBlank < Base
33
+ include RangeHelp
34
+ extend AutoCorrector
35
+ extend TargetRailsVersion
36
+
37
+ MSG = 'Use `%<preferred_method>s` instead.'
38
+ RESTRICT_ON_SEND = %i[reject reject!].freeze
39
+
40
+ minimum_target_rails_version 6.1
41
+
42
+ def_node_matcher :reject_with_block?, <<~PATTERN
43
+ (block
44
+ (send _ {:reject :reject!})
45
+ $(args ...)
46
+ (send
47
+ $(lvar _) :blank?))
48
+ PATTERN
49
+
50
+ def_node_matcher :reject_with_block_pass?, <<~PATTERN
51
+ (send _ {:reject :reject!}
52
+ (block_pass
53
+ (sym :blank?)))
54
+ PATTERN
55
+
56
+ def on_send(node)
57
+ return unless bad_method?(node)
58
+
59
+ range = offense_range(node)
60
+ preferred_method = preferred_method(node)
61
+ add_offense(range, message: format(MSG, preferred_method: preferred_method)) do |corrector|
62
+ corrector.replace(range, preferred_method)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def bad_method?(node)
69
+ return true if reject_with_block_pass?(node)
70
+
71
+ if (arguments, receiver_in_block = reject_with_block?(node.parent))
72
+ return use_single_value_block_argument?(arguments, receiver_in_block) ||
73
+ use_hash_value_block_argument?(arguments, receiver_in_block)
74
+ end
75
+
76
+ false
77
+ end
78
+
79
+ def use_single_value_block_argument?(arguments, receiver_in_block)
80
+ arguments.length == 1 && arguments[0].source == receiver_in_block.source
81
+ end
82
+
83
+ def use_hash_value_block_argument?(arguments, receiver_in_block)
84
+ arguments.length == 2 && arguments[1].source == receiver_in_block.source
85
+ end
86
+
87
+ def offense_range(node)
88
+ end_pos = node.parent&.block_type? ? node.parent.loc.expression.end_pos : node.loc.expression.end_pos
89
+
90
+ range_between(node.loc.selector.begin_pos, end_pos)
91
+ end
92
+
93
+ def preferred_method(node)
94
+ node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
95
+ end
96
+ end
97
+ end
98
+ end
99
+ 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
@@ -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,98 @@
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
+ :month, :months, :year, :years]
31
+
32
+ # @!method duration_arithmetic_argument?(node)
33
+ # Match duration subtraction or addition with current time.
34
+ #
35
+ # @example source that matches
36
+ # Time.current - 1.hour
37
+ #
38
+ # @example source that matches
39
+ # ::Time.zone.now + 1.hour
40
+ #
41
+ # @param node [RuboCop::AST::Node]
42
+ # @yield operator and duration
43
+ def_node_matcher :duration_arithmetic_argument?, <<~PATTERN
44
+ (send #time_current? ${ :+ :- } $#duration?)
45
+ PATTERN
46
+
47
+ # @!method duration?(node)
48
+ # Match a literal Duration
49
+ #
50
+ # @example source that matches
51
+ # 1.hour
52
+ #
53
+ # @example source that matches
54
+ # 9.5.weeks
55
+ #
56
+ # @param node [RuboCop::AST::Node]
57
+ # @return [Boolean] true if matches
58
+ def_node_matcher :duration?, '(send { int float (send nil _) } DURATIONS)'
59
+
60
+ # @!method time_current?(node)
61
+ # Match Time.current
62
+ #
63
+ # @example source that matches
64
+ # Time.current
65
+ #
66
+ # @example source that matches
67
+ # ::Time.zone.now
68
+ #
69
+ # @param node [RuboCop::AST::Node]
70
+ # @return [Boolean] true if matches
71
+ def_node_matcher :time_current?, <<~PATTERN
72
+ {
73
+ (send (const _ :Time) :current)
74
+ (send (send (const _ :Time) :zone) :now)
75
+ }
76
+ PATTERN
77
+
78
+ def on_send(node)
79
+ duration_arithmetic_argument?(node) do |*operation|
80
+ add_offense(node) do |corrector|
81
+ corrector.replace(node.source_range, corrected_source(*operation))
82
+ end
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def corrected_source(operator, duration)
89
+ if operator == :-
90
+ "#{duration.source}.ago"
91
+ else
92
+ "#{duration.source}.from_now"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ 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)
@@ -55,6 +55,8 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def active_model_error?(node)
58
+ return false if node.nil?
59
+
58
60
  node.send_type? && node.method?(:errors)
59
61
  end
60
62
  end
@@ -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)
@@ -23,22 +23,22 @@ module RuboCop
23
23
 
24
24
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
25
25
  (block
26
- ({send csend} _ :each_with_object (hash))
26
+ (call _ :each_with_object (hash))
27
27
  (args (arg $_el) (arg _memo))
28
- ({send csend} (lvar _memo) :[]= $!`_memo (lvar _el)))
28
+ (call (lvar _memo) :[]= $!`_memo (lvar _el)))
29
29
  PATTERN
30
30
 
31
31
  def_node_matcher :on_bad_to_h, <<~PATTERN
32
32
  (block
33
- ({send csend} _ :to_h)
33
+ (call _ :to_h)
34
34
  (args (arg $_el))
35
35
  (array $_ (lvar _el)))
36
36
  PATTERN
37
37
 
38
38
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
39
- ({send csend}
39
+ (call
40
40
  (block
41
- ({send csend} _ {:map :collect})
41
+ (call _ {:map :collect})
42
42
  (args (arg $_el))
43
43
  (array $_ (lvar _el)))
44
44
  :to_h)
@@ -49,7 +49,7 @@ module RuboCop
49
49
  (const _ :Hash)
50
50
  :[]
51
51
  (block
52
- ({send csend} _ {:map :collect})
52
+ (call _ {:map :collect})
53
53
  (args (arg $_el))
54
54
  (array $_ (lvar _el))))
55
55
  PATTERN
@@ -26,22 +26,22 @@ module RuboCop
26
26
 
27
27
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
28
28
  (block
29
- ({send csend} _ :each_with_object (hash))
29
+ (call _ :each_with_object (hash))
30
30
  (args (arg $_el) (arg _memo))
31
- ({send csend} (lvar _memo) :[]= (lvar _el) $!`_memo))
31
+ (call (lvar _memo) :[]= (lvar _el) $!`_memo))
32
32
  PATTERN
33
33
 
34
34
  def_node_matcher :on_bad_to_h, <<~PATTERN
35
35
  (block
36
- ({send csend} _ :to_h)
36
+ (call _ :to_h)
37
37
  (args (arg $_el))
38
38
  (array (lvar _el) $_))
39
39
  PATTERN
40
40
 
41
41
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
42
- ({send csend}
42
+ (call
43
43
  (block
44
- ({send csend} _ {:map :collect})
44
+ (call _ {:map :collect})
45
45
  (args (arg $_el))
46
46
  (array (lvar _el) $_))
47
47
  :to_h)
@@ -52,7 +52,7 @@ module RuboCop
52
52
  (const _ :Hash)
53
53
  :[]
54
54
  (block
55
- ({send csend} _ {:map :collect})
55
+ (call _ {:map :collect})
56
56
  (args (arg $_el))
57
57
  (array (lvar _el) $_)))
58
58
  PATTERN