rubocop-rails 2.37.0 → 2.38.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9258d56d31fc4adeef0526b551dbe652abe70dd4599debc25ee39be54b743330
4
- data.tar.gz: ab2d60fe9b2a61ac47f8f15f8753fb60dd26e278d5452d23501f59a398fd7b0a
3
+ metadata.gz: 934845fbf3f9e2a700b98f36bcd02c55bf2ec0ed4955931830e36463f3dc2793
4
+ data.tar.gz: a4623739f29604fa7869bc431442feea890337223628839e500672f2f74f1783
5
5
  SHA512:
6
- metadata.gz: 7ba4c487f87fdbd58130ba06fc0184c9c5b5dabe5ee3934aa03930c27467072a7f89ac77ba423fe89f69ad9739c2dfcbeac1bb947e64804757e6efbdb47b47a5
7
- data.tar.gz: 223e0693edfd7de08b9d6a770fbdd7ced5c6422cab60d5074657ef65dd7b4ce538c8f61c37cc15156d63824e1d4060a654bca1fef7830d1b587e7ca84d516f62
6
+ metadata.gz: bf70f7da40827b323bf43d3ee9718177a11aeed5417f6a332d31a2a8edbd63733472819fd6bc658bcf1c47451b3f38125f2b84e1ac9de1aa6e8eb35593d52c32
7
+ data.tar.gz: 3db22831cd73b6c83abcba17c13fdf1b239fd94197644f311bc259433fdf78430f7e0051bdc9e27dbbb490e1e5c6887aa1e573a9d85512267fbfb78f09cfb871
@@ -11,6 +11,29 @@ module RuboCop
11
11
  # See https://github.com/rubocop/rubocop/pull/11289
12
12
  TARGET_GEM_NAME = 'railties' # :nodoc:
13
13
 
14
+ # Used when the target Rails version cannot be detected from `AllCops: TargetRailsVersion`
15
+ # or from the `railties` version in the target's lockfile.
16
+ DEFAULT_RAILS_VERSION = 5.0
17
+
18
+ # Resolves the target Rails version as a `major.minor` Float, using only stable RuboCop core APIs.
19
+ # Precedence: `AllCops: TargetRailsVersion`, then the `railties` version from the target's lockfile,
20
+ # then `DEFAULT_RAILS_VERSION`.
21
+ def self.resolve(config)
22
+ target_rails_version = config.for_all_cops['TargetRailsVersion']
23
+ return target_rails_version.to_f if target_rails_version
24
+
25
+ gem_versions_in_target = config.gem_versions_in_target
26
+ railties_version = gem_versions_in_target && gem_versions_in_target[TARGET_GEM_NAME]
27
+ return DEFAULT_RAILS_VERSION unless railties_version
28
+
29
+ segments = railties_version.segments
30
+ Float("#{segments[0]}.#{segments[1]}")
31
+ end
32
+
33
+ def self.extended(base)
34
+ base.include(InstanceMethods)
35
+ end
36
+
14
37
  def minimum_target_rails_version(version)
15
38
  case version
16
39
  when Integer, Float then requires_gem(TARGET_GEM_NAME, ">= #{version}")
@@ -26,6 +49,35 @@ module RuboCop
26
49
 
27
50
  gem_requirement.satisfied_by?(Gem::Version.new(version))
28
51
  end
52
+
53
+ # Instance methods for cops that use the resolved target Rails version at runtime,
54
+ # included automatically when a cop extends `TargetRailsVersion`.
55
+ # On RuboCop < 2.0, `#target_rails_version` shadows `RuboCop::Cop::Base#target_rails_version`,
56
+ # so the resolution is owned by rubocop-rails on all supported RuboCop versions.
57
+ module InstanceMethods
58
+ def target_rails_version
59
+ @target_rails_version ||= TargetRailsVersion.resolve(config)
60
+ end
61
+
62
+ private
63
+
64
+ # Overrides `RuboCop::Cop::Base` so that `railties` requirements declared via
65
+ # `minimum_target_rails_version` are checked against the resolved target Rails version
66
+ # instead of the lockfile alone. This keeps `AllCops: TargetRailsVersion` effective for
67
+ # version-gated cops, including in projects without a lockfile.
68
+ def target_satisfies_all_gem_version_requirements?
69
+ self.class.gem_requirements.all? do |gem_name, version_requirement|
70
+ if gem_name == TARGET_GEM_NAME
71
+ version_requirement.satisfied_by?(Gem::Version.new(target_rails_version.to_s))
72
+ else
73
+ gem_versions_in_target = config.gem_versions_in_target
74
+ gem_version_in_target = gem_versions_in_target && gem_versions_in_target[gem_name]
75
+
76
+ gem_version_in_target && version_requirement.satisfied_by?(gem_version_in_target)
77
+ end
78
+ end
79
+ end
80
+ end
29
81
  end
30
82
  end
31
83
  end
@@ -19,6 +19,7 @@ module RuboCop
19
19
  # ActiveSupport.on_load(:active_record) { include MyClass }
20
20
  class ActiveSupportOnLoad < Base
21
21
  extend AutoCorrector
22
+ extend TargetRailsVersion
22
23
 
23
24
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
24
25
  RESTRICT_ON_SEND = %i[prepend include extend].freeze
@@ -66,6 +66,7 @@ module RuboCop
66
66
  class BulkChangeTable < Base
67
67
  include DatabaseTypeResolvable
68
68
  include MigrationsHelper
69
+ extend TargetRailsVersion
69
70
 
70
71
  MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
71
72
  You can combine alter queries using `bulk: true` options.
@@ -35,6 +35,7 @@ module RuboCop
35
35
  class DeprecatedActiveModelErrorsMethods < Base
36
36
  include RangeHelp
37
37
  extend AutoCorrector
38
+ extend TargetRailsVersion
38
39
 
39
40
  MSG = 'Avoid manipulating ActiveModel errors as hash directly.'
40
41
  AUTOCORRECTABLE_METHODS = %i[<< clear keys].freeze
@@ -25,6 +25,7 @@ module RuboCop
25
25
  #
26
26
  class EnumHash < Base
27
27
  extend AutoCorrector
28
+ extend TargetRailsVersion
28
29
 
29
30
  MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
30
31
  RESTRICT_ON_SEND = %i[enum].freeze
@@ -138,6 +138,8 @@ module RuboCop
138
138
  # has_many :posts, -> { order(published_at: :desc) }
139
139
  # end
140
140
  class InverseOf < Base
141
+ extend TargetRailsVersion
142
+
141
143
  SPECIFY_MSG = 'Specify an `:inverse_of` option.'
142
144
  NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to use `inverse_of: false`.'
143
145
  RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
@@ -45,22 +45,62 @@ module RuboCop
45
45
  return if with_dependencies?(task_method)
46
46
 
47
47
  add_offense(task_method) do |corrector|
48
- if with_arguments?(task_method)
49
- new_task_dependency = correct_task_arguments_dependency(task_method)
50
- corrector.replace(task_arguments(task_method), new_task_dependency)
51
- else
52
- task_name = task_method.first_argument
53
- new_task_dependency = correct_task_dependency(task_name)
54
- corrector.replace(task_name, new_task_dependency)
55
- end
48
+ autocorrect(corrector, task_method)
56
49
  end
57
50
  end
58
51
  end
59
52
 
60
53
  private
61
54
 
62
- def correct_task_arguments_dependency(task_method)
63
- "#{task_arguments(task_method).source} => :environment"
55
+ def autocorrect(corrector, task_method)
56
+ task_name, *task_arguments = task_method.arguments
57
+
58
+ if task_arguments.empty?
59
+ correct_task_name(corrector, task_name)
60
+ else
61
+ correct_task_arguments(corrector, task_arguments)
62
+ end
63
+ end
64
+
65
+ def correct_task_name(corrector, task_name)
66
+ if task_name.hash_type?
67
+ correct_empty_dependencies(corrector, task_name)
68
+ else
69
+ corrector.replace(task_name, correct_task_dependency(task_name))
70
+ end
71
+ end
72
+
73
+ def correct_task_arguments(corrector, task_arguments)
74
+ if task_arguments.one?
75
+ correct_single_task_argument(corrector, task_arguments.first)
76
+ elsif task_arguments.all? { |argument| argument.type?(:sym, :str) }
77
+ corrector.replace(task_arguments_range(task_arguments), task_arguments_dependency(task_arguments))
78
+ end
79
+ end
80
+
81
+ def correct_single_task_argument(corrector, argument)
82
+ if argument.hash_type?
83
+ correct_empty_dependencies(corrector, argument)
84
+ elsif argument.array_type?
85
+ corrector.replace(argument, "#{argument.source} => :environment")
86
+ elsif argument.type?(:sym, :str)
87
+ corrector.replace(argument, task_arguments_dependency([argument]))
88
+ end
89
+ end
90
+
91
+ def task_arguments_dependency(task_arguments)
92
+ "[#{task_arguments.map(&:source).join(', ')}] => :environment"
93
+ end
94
+
95
+ def correct_empty_dependencies(corrector, hash_node)
96
+ dependencies = hash_node.pairs.first&.value
97
+ return unless dependencies&.array_type? && dependencies.values.empty?
98
+
99
+ corrector.replace(dependencies, '[:environment]')
100
+ end
101
+
102
+ def task_arguments_range(task_arguments)
103
+ task_arguments.first.source_range.join(task_arguments.last.source_range)
64
104
  end
65
105
 
66
106
  def correct_task_dependency(task_name)
@@ -88,14 +128,6 @@ module RuboCop
88
128
  end
89
129
  end
90
130
 
91
- def task_arguments(node)
92
- node.arguments[1]
93
- end
94
-
95
- def with_arguments?(node)
96
- node.arguments.size > 1 && node.arguments[1].array_type?
97
- end
98
-
99
131
  def with_dependencies?(node)
100
132
  first_arg = node.first_argument
101
133
  return false unless first_arg
@@ -52,6 +52,8 @@ module RuboCop
52
52
  return if within_shadowing_method?(node)
53
53
 
54
54
  add_offense(node, message: build_message(node)) do |corrector|
55
+ next if nested_read_write_attribute?(node)
56
+
55
57
  corrector.replace(node, node_replacement(node))
56
58
  end
57
59
  end
@@ -106,6 +108,12 @@ module RuboCop
106
108
  def write_attribute_replacement(node)
107
109
  "self[#{node.first_argument.source}] = #{node.last_argument.source}"
108
110
  end
111
+
112
+ def nested_read_write_attribute?(node)
113
+ node.each_descendant(:send).any? do |descendant|
114
+ read_write_attribute?(descendant) && !within_shadowing_method?(descendant)
115
+ end
116
+ end
109
117
  end
110
118
  end
111
119
  end
@@ -152,6 +152,7 @@ module RuboCop
152
152
  # end
153
153
  class ReversibleMigration < Base
154
154
  include MigrationsHelper
155
+ extend TargetRailsVersion
155
156
 
156
157
  MSG = '%<action>s is not reversible.'
157
158
 
@@ -64,6 +64,8 @@ module RuboCop
64
64
  # end
65
65
  #
66
66
  class TransactionExitStatement < Base
67
+ extend TargetRailsVersion
68
+
67
69
  MSG = 'Exit statement `%<statement>s` is not allowed. Use `raise` (rollback) or `next` (commit).'
68
70
  BUILT_IN_TRANSACTION_METHODS = %i[transaction with_lock].freeze
69
71
 
@@ -34,6 +34,8 @@ module RuboCop
34
34
  # end
35
35
  #
36
36
  class UnknownEnv < Base
37
+ extend TargetRailsVersion
38
+
37
39
  MSG = 'Unknown environment `%<name>s`.'
38
40
  MSG_SIMILAR = 'Unknown environment `%<name>s`. Did you mean `%<similar>s`?'
39
41
 
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.37.0'
7
+ STRING = '2.38.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.37.0
4
+ version: 2.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -271,7 +271,7 @@ metadata:
271
271
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
272
272
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
273
273
  source_code_uri: https://github.com/rubocop/rubocop-rails/
274
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.37/
274
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.38/
275
275
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
276
276
  rubygems_mfa_required: 'true'
277
277
  default_lint_roller_plugin: RuboCop::Rails::Plugin