rubocop-rails 2.12.3 → 2.13.2

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 (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