rubocop-nathank 1.0.0 → 2.0.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 +4 -4
- data/CHANGELOG.md +26 -1
- data/lib/rubocop/nathank/config.yaml +4 -5
- data/lib/rubocop/nathank/support/prism_source.rb +7 -2
- data/lib/rubocop/nathank/version.rb +1 -1
- data/lib/rubocop/nathank.rb +0 -1
- data/readme.md +24 -1
- metadata +1 -2
- data/lib/rubocop/nathank/single_line_methods_for_events.rb +0 -168
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0cec356ff42dae5257d46997d1266931e657bacc97da9dc7f275d487782ab80e
|
|
4
|
+
data.tar.gz: 5dafe29111e93b1b6d4c6373076999e52ad60451b8160f840d7478194754a71f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f3c75b4d53b7f1dad397a9f6a96e3510cbbce0cbf862b3d508eee4c3825893cc529eb59ab4cfff4853972ac937e5638456e258f6fffcd766df39a3d10f72c9a
|
|
7
|
+
data.tar.gz: 92bb4cbfdd73e40a589f4869a96d6a7c95d0fe633c8cbc0f001dd7cc383134b184c6213ab296a441e90ca54d1ded94be37effc7674f726e7b5af51e58cf6c640
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0] - 2026-10-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The plugin sets `AllCops/DisabledByDefault`, which is what it was always for:
|
|
15
|
+
RuboCop's own cops are off and the house cops are on, so a project needs
|
|
16
|
+
nothing in its `.rubocop.yml` beyond the `plugins:` entry.
|
|
17
|
+
- A project must no longer set `AllCops/DisabledByDefault` itself. Your config is
|
|
18
|
+
merged over the plugin's, so setting it again takes the house cops off along
|
|
19
|
+
with RuboCop's and the run reports nothing. Remove it, and remove the
|
|
20
|
+
`Nathank/*: Enabled: true` lines it made necessary.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
|
|
24
|
+
- `Nathank/SingleLineMethodsForEvents`. Endless methods are a matter of taste
|
|
25
|
+
rather than house style, and the cop raised on any file holding a method with
|
|
26
|
+
its own `rescue`, which took it out of the run without saying so. Remove any
|
|
27
|
+
`.rubocop.yml` entry for it.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `prism_statements` reads the statements of a `BeginNode`, so a method with its
|
|
32
|
+
own `rescue` or `ensure` no longer raises `undefined method 'body'`.
|
|
33
|
+
|
|
10
34
|
## [1.0.0] - 2026-10-27
|
|
11
35
|
|
|
12
36
|
### Added
|
|
@@ -26,5 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
26
50
|
lint_roller plugin class is now `RuboCop::Nathank::Plugin`, referenced by the
|
|
27
51
|
gemspec's `default_lint_roller_plugin` metadata.
|
|
28
52
|
|
|
29
|
-
[Unreleased]: https://github.com/n-at-han-k/rubocop-nathank/compare/
|
|
53
|
+
[Unreleased]: https://github.com/n-at-han-k/rubocop-nathank/compare/v2.0.0...HEAD
|
|
54
|
+
[2.0.0]: https://github.com/n-at-han-k/rubocop-nathank/compare/v1.0.0...v2.0.0
|
|
30
55
|
[1.0.0]: https://github.com/n-at-han-k/rubocop-nathank/releases/tag/v1.0.0
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
# This is the default configuration file for rubocop-nathank.
|
|
4
4
|
# You can override any of these settings in your .rubocop.yml file.
|
|
5
5
|
|
|
6
|
+
# The whole point of the plugin: the house style is what these cops say, not what RuboCop's defaults say, so the defaults are off and the cops below are on. A project wanting one of the defaults back enables it by name in its own .rubocop.yml, which is merged over this. It must not set DisabledByDefault itself: that would take these cops off too.
|
|
7
|
+
AllCops:
|
|
8
|
+
DisabledByDefault: true
|
|
9
|
+
|
|
6
10
|
Nathank/BeginBlockAssignment:
|
|
7
11
|
Description: "Bans assigning a `begin...end` expression; the assignment goes inside the block."
|
|
8
12
|
Enabled: true
|
|
@@ -41,11 +45,6 @@ Nathank/NoReturn:
|
|
|
41
45
|
Enabled: true
|
|
42
46
|
VersionAdded: "0.1.0"
|
|
43
47
|
|
|
44
|
-
Nathank/SingleLineMethodsForEvents:
|
|
45
|
-
Description: "A one-expression method body uses the endless form."
|
|
46
|
-
Enabled: true
|
|
47
|
-
VersionAdded: "0.1.0"
|
|
48
|
-
|
|
49
48
|
Nathank/UseTap:
|
|
50
49
|
Description: "Requires `tap` where a method assigns a local on its first line and returns it on its last."
|
|
51
50
|
Enabled: true
|
|
@@ -98,11 +98,16 @@ module RuboCop
|
|
|
98
98
|
|
|
99
99
|
# The statements of a body, whether it holds one or many.
|
|
100
100
|
#
|
|
101
|
-
#
|
|
101
|
+
# A method with its own `rescue` has a BeginNode body, whose statements are one level in.
|
|
102
|
+
#
|
|
103
|
+
# @parameter body [Prism::Node | Nil] the body of a method, module, or block.
|
|
102
104
|
# @returns [Array(Prism::Node)] its statements, which is empty when there is no body.
|
|
103
105
|
def prism_statements(body)
|
|
104
|
-
|
|
106
|
+
case body
|
|
107
|
+
when nil
|
|
105
108
|
[]
|
|
109
|
+
when ::Prism::BeginNode
|
|
110
|
+
self.prism_statements(body.statements)
|
|
106
111
|
else
|
|
107
112
|
body.body
|
|
108
113
|
end
|
data/lib/rubocop/nathank.rb
CHANGED
|
@@ -12,7 +12,6 @@ require_relative "nathank/comment_structure"
|
|
|
12
12
|
require_relative "nathank/no_conditional_assignment"
|
|
13
13
|
require_relative "nathank/no_modifier_if"
|
|
14
14
|
require_relative "nathank/no_return"
|
|
15
|
-
require_relative "nathank/single_line_methods_for_events"
|
|
16
15
|
require_relative "nathank/use_tap"
|
|
17
16
|
|
|
18
17
|
# @namespace
|
data/readme.md
CHANGED
|
@@ -16,6 +16,30 @@ plugins:
|
|
|
16
16
|
- rubocop-nathank
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
That is the whole configuration. The plugin sets `AllCops/DisabledByDefault`, so RuboCop's own
|
|
20
|
+
cops are off and the ones below are on: the house style is what these cops say rather than what
|
|
21
|
+
the defaults say, and a project does not have to list them to get them.
|
|
22
|
+
|
|
23
|
+
A default you want back is enabled by name, and a house cop you do not want is disabled the same
|
|
24
|
+
way, because your `.rubocop.yml` is merged over the plugin's:
|
|
25
|
+
|
|
26
|
+
``` yaml
|
|
27
|
+
# .rubocop.yml
|
|
28
|
+
plugins:
|
|
29
|
+
- rubocop-nathank
|
|
30
|
+
|
|
31
|
+
Style/StringLiterals:
|
|
32
|
+
Enabled: true
|
|
33
|
+
EnforcedStyle: double_quotes
|
|
34
|
+
|
|
35
|
+
Nathank/NoReturn:
|
|
36
|
+
Enabled: false
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
What you must not do is set `AllCops/DisabledByDefault` yourself. The plugin already sets it, and
|
|
40
|
+
your file is merged over the plugin's: setting it again takes the plugin's own cops off along with
|
|
41
|
+
RuboCop's, and you get a run which inspects every file and reports nothing.
|
|
42
|
+
|
|
19
43
|
## Available Cops
|
|
20
44
|
|
|
21
45
|
* `Nathank/BeginBlockAssignment` - no assigning a `begin...end`; the assignment goes inside.
|
|
@@ -23,7 +47,6 @@ plugins:
|
|
|
23
47
|
* `Nathank/NoModifierIf` - no modifier `if` / `unless`, so a guard cannot hide off the right edge.
|
|
24
48
|
* `Nathank/NoReturn` - no `return`; the last expression is the return value.
|
|
25
49
|
* `Nathank/UseTap` - `tap` where a method assigns a local and returns it.
|
|
26
|
-
* `Nathank/SingleLineMethodsForEvents` - a one-expression body uses the endless form.
|
|
27
50
|
* `Nathank/CommentStructure` - how a comment is built: unwrapped paragraphs, a summary no longer
|
|
28
51
|
than the code earns, and annotations on a documented interface.
|
|
29
52
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-nathank
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan Kidd
|
|
@@ -81,7 +81,6 @@ files:
|
|
|
81
81
|
- lib/rubocop/nathank/no_modifier_if.rb
|
|
82
82
|
- lib/rubocop/nathank/no_return.rb
|
|
83
83
|
- lib/rubocop/nathank/plugin.rb
|
|
84
|
-
- lib/rubocop/nathank/single_line_methods_for_events.rb
|
|
85
84
|
- lib/rubocop/nathank/support/prism_source.rb
|
|
86
85
|
- lib/rubocop/nathank/use_tap.rb
|
|
87
86
|
- lib/rubocop/nathank/version.rb
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Released under the Apache License, Version 2.0.
|
|
4
|
-
# Copyright, 2026, by General Intelligence Systems.
|
|
5
|
-
|
|
6
|
-
require_relative "support/prism_source"
|
|
7
|
-
|
|
8
|
-
module RuboCop
|
|
9
|
-
module Nathank
|
|
10
|
-
# Requires a method whose body is a single expression to use the endless method form.
|
|
11
|
-
#
|
|
12
|
-
# @example
|
|
13
|
-
# # bad
|
|
14
|
-
# def event_type
|
|
15
|
-
# "m.room.message"
|
|
16
|
-
# end
|
|
17
|
-
# # good
|
|
18
|
-
# def event_type = "m.room.message"
|
|
19
|
-
class SingleLineMethodsForEvents < ::RuboCop::Cop::Base
|
|
20
|
-
include PrismSource
|
|
21
|
-
extend ::RuboCop::Cop::AutoCorrector
|
|
22
|
-
|
|
23
|
-
MSG = "A method whose body is one expression must use the endless form: `def name = expression`."
|
|
24
|
-
|
|
25
|
-
# Expressions which can follow ` = ` unchanged. Anything with its own `end`, or with lower precedence than the assignment, is left as a block: what it would parse as is not what it says as a body.
|
|
26
|
-
ACCEPTED_BODIES = [
|
|
27
|
-
::Prism::ArrayNode,
|
|
28
|
-
::Prism::CallNode,
|
|
29
|
-
::Prism::ClassVariableReadNode,
|
|
30
|
-
::Prism::ConstantPathNode,
|
|
31
|
-
::Prism::ConstantReadNode,
|
|
32
|
-
::Prism::FalseNode,
|
|
33
|
-
::Prism::FloatNode,
|
|
34
|
-
::Prism::GlobalVariableReadNode,
|
|
35
|
-
::Prism::HashNode,
|
|
36
|
-
::Prism::ImaginaryNode,
|
|
37
|
-
::Prism::InstanceVariableReadNode,
|
|
38
|
-
::Prism::IntegerNode,
|
|
39
|
-
::Prism::InterpolatedStringNode,
|
|
40
|
-
::Prism::InterpolatedSymbolNode,
|
|
41
|
-
::Prism::InterpolatedXStringNode,
|
|
42
|
-
::Prism::LocalVariableReadNode,
|
|
43
|
-
::Prism::NilNode,
|
|
44
|
-
::Prism::RangeNode,
|
|
45
|
-
::Prism::RationalNode,
|
|
46
|
-
::Prism::RegularExpressionNode,
|
|
47
|
-
::Prism::SelfNode,
|
|
48
|
-
::Prism::StringNode,
|
|
49
|
-
::Prism::SymbolNode,
|
|
50
|
-
::Prism::TrueNode,
|
|
51
|
-
::Prism::XStringNode,
|
|
52
|
-
].freeze
|
|
53
|
-
|
|
54
|
-
def on_new_investigation
|
|
55
|
-
self.prism_each(::Prism::DefNode).each do |node|
|
|
56
|
-
if self.rewriteable?(node)
|
|
57
|
-
self.report(node)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
private
|
|
63
|
-
|
|
64
|
-
# The one expression a method's body is, when that is what it is.
|
|
65
|
-
def sole_expression(node)
|
|
66
|
-
statements = self.prism_statements(node.body)
|
|
67
|
-
|
|
68
|
-
if statements.size == 1
|
|
69
|
-
statements.first
|
|
70
|
-
else
|
|
71
|
-
nil
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# A method is rewritten only when it is not endless, not a setter, and has one non-heredoc body expression on one line.
|
|
76
|
-
def rewriteable?(node)
|
|
77
|
-
body = self.sole_expression(node)
|
|
78
|
-
|
|
79
|
-
!self.endless?(node) &&
|
|
80
|
-
!self.setter?(node.name) &&
|
|
81
|
-
!body.nil? &&
|
|
82
|
-
ACCEPTED_BODIES.any?{|klass| body.is_a?(klass)} &&
|
|
83
|
-
self.single_line?(node, body) &&
|
|
84
|
-
!self.prism_heredoc?(body)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# An endless method is written with `=` rather than a body closed by `end`.
|
|
88
|
-
def endless?(node)
|
|
89
|
-
!node.equal_loc.nil?
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# The offense is reported either way; the correction is withheld when a comment sits inside the method, since replacing the method would take the comment with it.
|
|
93
|
-
def report(node)
|
|
94
|
-
range = self.prism_node_range(node)
|
|
95
|
-
|
|
96
|
-
if self.comment?(node)
|
|
97
|
-
add_offense(range)
|
|
98
|
-
else
|
|
99
|
-
add_offense(range) do |corrector|
|
|
100
|
-
corrector.replace(range, self.endless_form(node))
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# `def foo=(value)` and `def []=(key, value)` have no endless form.
|
|
106
|
-
def setter?(name)
|
|
107
|
-
name == :[]= || /\A[a-zA-Z_]\w*=\z/.match?(name.to_s)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# The body on one line, and the signature on one line too, so that the rewritten method is still a single line.
|
|
111
|
-
def single_line?(node, body)
|
|
112
|
-
body.location.start_line == body.location.end_line &&
|
|
113
|
-
!self.signature_source(node).include?("\n")
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# The source of the signature, e.g. `def build(**fields)`, without the gap before the body.
|
|
117
|
-
def signature_source(node)
|
|
118
|
-
source = processed_source.buffer.source
|
|
119
|
-
|
|
120
|
-
# byteslice, because every offset here is one of Prism's and those count bytes, where `String#[]` counts characters.
|
|
121
|
-
source.byteslice(node.location.start_offset...self.signature_end(node))
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def signature_end(node)
|
|
125
|
-
ending = node.name_loc.end_offset
|
|
126
|
-
|
|
127
|
-
if parameters = node.parameters
|
|
128
|
-
[ending, parameters.location.end_offset].max
|
|
129
|
-
else
|
|
130
|
-
ending
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def comment?(node)
|
|
135
|
-
self.prism_comments.any? do |comment|
|
|
136
|
-
(node.location.start_line..node.location.end_line).cover?(comment.location.start_line)
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def endless_form(node)
|
|
141
|
-
"#{self.signature(node)} = #{self.prism_source(self.sole_expression(node))}"
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def signature(node)
|
|
145
|
-
"#{self.receiver_prefix(node)}#{node.name}#{self.arguments_suffix(node)}"
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def receiver_prefix(node)
|
|
149
|
-
if node.receiver
|
|
150
|
-
"def #{self.prism_source(node.receiver)}."
|
|
151
|
-
else
|
|
152
|
-
"def "
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Bare arguments are ambiguous before ` = `; parenthesize them.
|
|
157
|
-
def arguments_suffix(node)
|
|
158
|
-
parameters = node.parameters
|
|
159
|
-
|
|
160
|
-
if parameters.nil?
|
|
161
|
-
""
|
|
162
|
-
else
|
|
163
|
-
"(#{self.prism_source(parameters)})"
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|