rubocop-ordered_methods 0.8 → 0.10

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: fc5f933eab16f22d4ce9e90bac7b7da25af7af45f7fd1204974ea26d98287476
4
- data.tar.gz: c3e5a535a8667acf2c88e8c127219b8b8e330d10e6575df5748391ec1c7758ba
3
+ metadata.gz: c43226dde578a4844ba7c579a7fa5dfb52f05dce2e84d9a68f1e250e272d4e58
4
+ data.tar.gz: 636542d7c4be5982d3424703ba2062c72c4fdbfcf4f289a5b593050b68c16969
5
5
  SHA512:
6
- metadata.gz: c83ba399a7357517935466f59e4be28b75fbb399a571041b54ba4be1c387797b1867be69fcac2abe8e60e89572b34d35d9a327a171880cec2d9807ee258766ca
7
- data.tar.gz: dc2897b646af1eb6e2b84dbe2e693be8a0a48de8645caf9551557c6dfe36809a34c92c4166428f9f0b17bd541c3a5c8bf60d349ea3db3286c09ebb4897467e7e
6
+ metadata.gz: 1eced8a7bb0e4a33bf58cae6b0b0d90fd9814318635689450ee34646905a5a41fbd97189c51d9763706f307d4ed4c3032e3eb02af2aa5ce79c373f12bd5f1019
7
+ data.tar.gz: 4c528b7368611c20b2ab790f28502f3c44e4e6546bcbff35b199c749932583bdea36cc5d441e598efb7b7e199b63fbac97f50a6534b206052f60474ebe24147a
@@ -0,0 +1,46 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
8
+
9
+ jobs:
10
+ rspec:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby:
15
+ - "2.7"
16
+ - "3.0"
17
+ - "3.1"
18
+ - "3.2"
19
+
20
+ name: "Ruby ${{ matrix.ruby }}: run rspec"
21
+ steps:
22
+ - uses: actions/checkout@v3
23
+ - uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: "${{ matrix.ruby }}"
26
+ bundler-cache: true
27
+ - run: bundle exec rspec
28
+
29
+ rubocop:
30
+ runs-on: ubuntu-latest
31
+ strategy:
32
+ matrix:
33
+ ruby:
34
+ - "2.7"
35
+ - "3.0"
36
+ - "3.1"
37
+ - "3.2"
38
+
39
+ name: "Ruby ${{ matrix.ruby }}: run rubocop"
40
+ steps:
41
+ - uses: actions/checkout@v3
42
+ - uses: ruby/setup-ruby@v1
43
+ with:
44
+ ruby-version: "${{ matrix.ruby }}"
45
+ bundler-cache: true
46
+ - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -5,7 +5,16 @@ require: rubocop-ordered_methods
5
5
  AllCops:
6
6
  NewCops: enable
7
7
  SuggestExtensions: false
8
- TargetRubyVersion: 2.4
8
+ TargetRubyVersion: 2.7
9
+
10
+ # Subtle, left to author's discretion. In a long method with many guard clauses,
11
+ # a blank line may help. But, in a short method, especially with only a single
12
+ # guard clause, a blank line can be disruptive.
13
+ Layout/EmptyLineAfterGuardClause:
14
+ Enabled: false
15
+
16
+ Layout/LineEndStringConcatenationIndentation:
17
+ EnforcedStyle: indented
9
18
 
10
19
  Metrics/BlockLength:
11
20
  Exclude:
@@ -18,3 +27,9 @@ Metrics/MethodLength:
18
27
  Naming/FileName:
19
28
  Exclude:
20
29
  - lib/rubocop-ordered_methods.rb
30
+
31
+ # Use the semantic style. If a block has side effects use `do`, and if it is
32
+ # pure use `{}`. This style is too nuanced for a linter, so the cop is
33
+ # disabled.
34
+ Style/BlockDelimiters:
35
+ Enabled: false
data/.travis.yml CHANGED
@@ -3,10 +3,10 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.3
7
6
  - 2.4
8
7
  - 2.5
9
8
  - 2.6
9
+ - 2.7
10
10
  script: bundle exec rake
11
11
  before_install:
12
12
  - gem install bundler || gem install bundler --version '< 2'
data/CHANGELOG.md CHANGED
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.10] - 2021-03-10
10
+
11
+ ### Removed
12
+
13
+ - Drop support for Ruby 2.4, 2.5, and 2.6
14
+
15
+ ### Added
16
+
17
+ - Support for custom method qualifiers ([#11](https://github.com/shanecav84/rubocop-ordered_methods/pull/11)). Thanks @Darhazer.
18
+ - Setup CI ([#12](https://github.com/shanecav84/rubocop-ordered_methods/pull/12)). Thanks @Darhazer.
19
+
20
+ ## [0.9] - 2021-03-10
21
+
22
+ ### Added
23
+
24
+ - Autocorrection support for Sorbet signatures
25
+
9
26
  ## [0.8] - 2021-02-01
10
27
 
11
28
  ### Fixed
data/Gemfile CHANGED
@@ -2,4 +2,9 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'bundler'
6
+ gem 'byebug'
7
+ gem 'rake', '~> 12.3.3'
8
+ gem 'rspec', '~> 3.0'
9
+
5
10
  gemspec
data/README.md CHANGED
@@ -74,8 +74,23 @@ rubocop --require rubocop-ordered_methods
74
74
 
75
75
  Name | Default value | Configurable values
76
76
  --- | --- | ---
77
- EnforcedStyle | `alphabetical` | `alphabetical`
78
- IgnoredMethods | `initialize` | Array
77
+ EnforcedStyle | `'alphabetical'` | `'alphabetical'`
78
+ IgnoredMethods | `['initialize']` | Array
79
+ MethodQualifiers | `[]` | Array
80
+ Signature | `nil` | `'sorbet'`, `nil`
81
+
82
+ #### Example
83
+
84
+ ```
85
+ # .rubocop.yml
86
+ Layout/OrderedMethods:
87
+ EnforcedStyle: alphabetical
88
+ IgnoredMethods:
89
+ - initialize
90
+ MethodQualifiers:
91
+ - memoize
92
+ Signature: sorbet
93
+ ```
79
94
 
80
95
  ### Corrector
81
96
 
@@ -125,6 +140,23 @@ protected :instance_a
125
140
  public :instance_a
126
141
  ```
127
142
 
143
+ #### Method qualifiers
144
+ Some gems (like `memery`, `memoist`, etc.) provide a DSL that modifies the method (e.g. for memoization).
145
+ Those DSL methods can be added to the `MethodQualifiers` configuration, and they will be respected.
146
+
147
+ E.g. the following source can be correctly ordered:
148
+ ```ruby
149
+ def b; end;
150
+ memoize def a;end
151
+ ```
152
+
153
+ #### Method signatures
154
+
155
+ Support for (Sorbet) method signatures was added to the corrector by
156
+ [#7](https://github.com/shanecav84/rubocop-ordered_methods/pull/7).
157
+ It is off by default due to performance concerns (not yet benchmarked). Enable
158
+ with `Signature: sorbet`.
159
+
128
160
  #### Caveats
129
161
 
130
162
  * The corrector will warn and refuse to order a method if it were to be
data/config/default.yml CHANGED
@@ -4,3 +4,4 @@ Layout/OrderedMethods:
4
4
  EnforcedStyle: 'alphabetical'
5
5
  IgnoredMethods:
6
6
  - initialize
7
+ Signature: ~
@@ -10,9 +10,11 @@ module RuboCop
10
10
  class OrderedMethodsCorrector
11
11
  include QualifierNodeMatchers
12
12
 
13
- def initialize(comments, siblings)
14
- @comments = comments
13
+ # @param cop_config ::RuboCop::Config
14
+ def initialize(comment_locations, siblings, cop_config)
15
+ @comment_locations = comment_locations
15
16
  @siblings = siblings
17
+ @cop_config = cop_config
16
18
  end
17
19
 
18
20
  def correct(node, previous_node)
@@ -45,13 +47,19 @@ module RuboCop
45
47
  (qualifier?(next_sibling) || alias?(next_sibling)) == node.method_name
46
48
  end
47
49
 
50
+ # @param node RuboCop::AST::DefNode
51
+ # @param source_range Parser::Source::Range
52
+ # @return Parser::Source::Range
48
53
  def join_comments(node, source_range)
49
- @comments[node].each do |comment|
54
+ @comment_locations[node.loc].each do |comment|
50
55
  source_range = source_range.join(comment.loc.expression)
51
56
  end
52
57
  source_range
53
58
  end
54
59
 
60
+ # @param node RuboCop::AST::DefNode
61
+ # @param source_range Parser::Source::Range
62
+ # @return Parser::Source::Range
55
63
  def join_modifiers_and_aliases(node, source_range)
56
64
  preceding_qualifier_index = node.sibling_index
57
65
  last_qualifier_index = find_last_qualifier_index(node)
@@ -64,12 +72,46 @@ module RuboCop
64
72
  source_range
65
73
  end
66
74
 
75
+ # @param node RuboCop::AST::DefNode
76
+ # @param source_range Parser::Source::Range
77
+ # @return Parser::Source::Range
78
+ def join_signature(node, source_range)
79
+ sib = node.left_sibling
80
+ if signature?(sib)
81
+ # If there is a comment directly above the sig, first calculate the
82
+ # range that covers both.
83
+ with_comment = join_comments(sib, sib.source_range)
84
+ source_range.join(with_comment)
85
+ else
86
+ source_range
87
+ end
88
+ end
89
+
90
+ def join_signature?
91
+ @cop_config['Signature'] == 'sorbet'
92
+ end
93
+
94
+ # @param node RuboCop::AST::DefNode
95
+ # @return Parser::Source::Range
67
96
  def join_surroundings(node)
68
97
  with_modifiers_and_aliases = join_modifiers_and_aliases(
69
98
  node,
70
99
  node.source_range
71
100
  )
72
- join_comments(node, with_modifiers_and_aliases)
101
+ with_comments = join_comments(node, with_modifiers_and_aliases)
102
+ if join_signature?
103
+ join_signature(node, with_comments)
104
+ else
105
+ with_comments
106
+ end
107
+ end
108
+
109
+ # https://sorbet.org/docs/sigs
110
+ # @param node RuboCop::AST::Node
111
+ def signature?(node)
112
+ return false unless node&.type == :block
113
+ child = node.children.first
114
+ child&.type == :send && child.method_name == :sig
73
115
  end
74
116
  end
75
117
  end
@@ -35,20 +35,27 @@ module RuboCop
35
35
  include RangeHelp
36
36
 
37
37
  COMPARISONS = {
38
- 'alphabetical' => lambda do |left_method, right_method|
39
- (left_method.method_name <=> right_method.method_name) != 1
38
+ 'alphabetical' => lambda do |left_node, right_node|
39
+ (method_name(left_node) <=> method_name(right_node)) != 1
40
40
  end
41
41
  }.freeze
42
42
  ERR_INVALID_COMPARISON = 'Invalid "Comparison" config for ' \
43
43
  "#{cop_name}. Expected one of: #{COMPARISONS.keys.join(', ')}".freeze
44
44
 
45
+ def self.method_name(node)
46
+ return node.method_name unless node.send_type?
47
+
48
+ node.first_argument.method_name
49
+ end
50
+
45
51
  def autocorrect(node)
46
- @corrector.correct(node, @previous_node)
52
+ _siblings, corrector = cache(node)
53
+ corrector.correct(node, @previous_node)
47
54
  end
48
55
 
49
56
  def on_begin(node)
50
- cache(node)
51
- consecutive_methods(@siblings) do |previous, current|
57
+ siblings, _corrector = cache(node)
58
+ consecutive_methods(siblings) do |previous, current|
52
59
  unless ordered?(previous, current)
53
60
  @previous_node = previous
54
61
  add_offense(
@@ -68,20 +75,33 @@ module RuboCop
68
75
  (node.send_type? && node.bare_access_modifier?)
69
76
  end
70
77
 
78
+ # rubocop:disable Metrics/MethodLength
71
79
  # Cache to avoid traversing the AST multiple times
72
80
  def cache(node)
73
- @cache ||= begin
74
- @siblings = node.children
75
-
76
- # Init the corrector with the cache to avoid traversing the AST in
77
- # the corrector. We always init the @corrector, even if
78
- # @options[:auto_correct] is nil, because `add_offense` always
79
- # attempts correction. This correction attempt is how RuboCop knows
80
- # if the offense can be labeled "[Correctable]".
81
- comments = processed_source.ast_with_comments
82
- @corrector = OrderedMethodsCorrector.new(comments, @siblings)
81
+ @cache ||= Hash.new do |h, key|
82
+ h[key.hash] = begin
83
+ siblings = node.children
84
+
85
+ # Init the corrector with the cache to avoid traversing the AST in
86
+ # the corrector.
87
+ #
88
+ # We always init the @corrector, even if @options[:auto_correct] is
89
+ # nil, because `add_offense` always attempts correction. This
90
+ # correction attempt is how RuboCop knows if the offense can be
91
+ # labeled "[Correctable]".
92
+ comment_locations = ::Parser::Source::Comment.associate_locations(
93
+ processed_source.ast,
94
+ processed_source.comments
95
+ )
96
+ corrector = OrderedMethodsCorrector.new(comment_locations, siblings, cop_config)
97
+
98
+ [siblings, corrector]
99
+ end
83
100
  end
101
+
102
+ @cache[node.hash]
84
103
  end
104
+ # rubocop:enable Metrics/MethodLength
85
105
 
86
106
  # We disable `Style/ExplicitBlockArgument` for performance. See
87
107
  # https://github.com/shanecav84/rubocop-ordered_methods/pull/5#pullrequestreview-562957146
@@ -99,10 +119,7 @@ module RuboCop
99
119
 
100
120
  def filter_relevant_nodes(nodes)
101
121
  nodes.select do |node|
102
- (
103
- (node.defs_type? || node.def_type?) &&
104
- !ignored_method?(node.method_name)
105
- ) || (node.send_type? && node.bare_access_modifier?)
122
+ relevant_node?(node) || (node.send_type? && qualifier_macro?(node))
106
123
  end
107
124
  end
108
125
 
@@ -134,6 +151,17 @@ module RuboCop
134
151
 
135
152
  comparison.call(left_method, right_method)
136
153
  end
154
+
155
+ def qualifier_macro?(node)
156
+ return true if node.bare_access_modifier?
157
+
158
+ cop_config['MethodQualifiers'].to_a.include?(node.method_name.to_s) &&
159
+ relevant_node?(node.first_argument)
160
+ end
161
+
162
+ def relevant_node?(node)
163
+ (node.defs_type? || node.def_type?) && !ignored_method?(node.method_name)
164
+ end
137
165
  end
138
166
  end
139
167
  end
@@ -20,9 +20,16 @@ module RuboCop
20
20
  def_node_matcher :alias_method?,
21
21
  '(send nil? {:alias_method} ... (sym $_method_name))'
22
22
  def_node_matcher :qualifier?, <<-PATTERN
23
- (send nil? {#{QUALIFIERS.map(&:inspect).join(' ')}}
24
- ... (sym $_method_name))
23
+ (send nil? #method_qualifier? ... (sym $_method_name))
25
24
  PATTERN
25
+
26
+ def method_qualifier?(name)
27
+ qualifiers.include?(name)
28
+ end
29
+
30
+ def qualifiers
31
+ @qualifiers ||= QUALIFIERS + @cop_config['MethodQualifiers'].to_a.map(&:to_sym)
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -5,14 +5,14 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'rubocop-ordered_methods'
8
- spec.version = '0.8'
8
+ spec.version = '0.10'
9
9
  spec.authors = ['Shane Cavanaugh']
10
10
  spec.email = ['shane@shanecav.net']
11
11
 
12
12
  spec.summary = 'Checks that methods are ordered alphabetically.'
13
13
  spec.homepage = 'https://github.com/shanecav84/rubocop-ordered_methods'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = '>= 2.4'
15
+ spec.required_ruby_version = '>= 2.7'
16
16
 
17
17
  # Specify which files should be added to the gem when it is released.
18
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added
@@ -28,7 +28,5 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_runtime_dependency 'rubocop', '>= 1.0'
30
30
 
31
- spec.add_development_dependency 'bundler'
32
- spec.add_development_dependency 'rake', '~> 12.3.3'
33
- spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.metadata['rubygems_mfa_required'] = 'true'
34
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-ordered_methods
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.8'
4
+ version: '0.10'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Cavanaugh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-01 00:00:00.000000000 Z
11
+ date: 2023-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -24,48 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: 12.3.3
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: 12.3.3
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
27
  description:
70
28
  email:
71
29
  - shane@shanecav.net
@@ -73,6 +31,7 @@ executables: []
73
31
  extensions: []
74
32
  extra_rdoc_files: []
75
33
  files:
34
+ - ".github/workflows/main.yml"
76
35
  - ".gitignore"
77
36
  - ".rakeTasks"
78
37
  - ".rubocop.yml"
@@ -96,7 +55,8 @@ files:
96
55
  homepage: https://github.com/shanecav84/rubocop-ordered_methods
97
56
  licenses:
98
57
  - MIT
99
- metadata: {}
58
+ metadata:
59
+ rubygems_mfa_required: 'true'
100
60
  post_install_message:
101
61
  rdoc_options: []
102
62
  require_paths:
@@ -105,14 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
65
  requirements:
106
66
  - - ">="
107
67
  - !ruby/object:Gem::Version
108
- version: '2.4'
68
+ version: '2.7'
109
69
  required_rubygems_version: !ruby/object:Gem::Requirement
110
70
  requirements:
111
71
  - - ">="
112
72
  - !ruby/object:Gem::Version
113
73
  version: '0'
114
74
  requirements: []
115
- rubygems_version: 3.1.2
75
+ rubygems_version: 3.2.33
116
76
  signing_key:
117
77
  specification_version: 4
118
78
  summary: Checks that methods are ordered alphabetically.