barsoom_utils 0.3.0.79 → 0.3.0.82
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/README.md +2 -0
- data/VERSION +1 -1
- data/lib/barsoom_utils/rubocop.rb +5 -0
- data/lib/rubocop/cop/barsoom/attr_extras_initialize.rb +63 -0
- data/lib/rubocop/cop/barsoom/inline_private_class_method.rb +62 -0
- data/shared_rubocop.yml +13 -0
- data/shared_rubocop_rails.yml +4 -0
- data/spec/rubocop/cop/barsoom/attr_extras_initialize_spec.rb +154 -0
- data/spec/rubocop/cop/barsoom/inline_private_class_method_spec.rb +73 -0
- data/spec/rubocop_helper.rb +8 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1757860af2eca9a71fffa1fa0c47a42e572314816f4dcf02d752d60c713ace67
|
|
4
|
+
data.tar.gz: 54eb7e03409786d5eb87c34affad0a363b2ac3d17ee19ec22c75c800fd171c6d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66619d39cab01f97f5da75ab364f6f5f215f1c7755683518e71fa5d91bd7c3c3689828e0088b2ee562b07cf891a852378e6ee5f1533e4ee6c40103cd28db2f25
|
|
7
|
+
data.tar.gz: ae669cd31bb669333412b0ea3f1bb6ebca0c584ed602685acf750f32106716fddd412ac657335ba7fc5b0ca32aa20bc4a0d11770746bf0ea8eddef37ddc1908c
|
data/README.md
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.3.0.
|
|
1
|
+
0.3.0.82
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module RuboCop
|
|
3
|
+
module Cop
|
|
4
|
+
module Barsoom
|
|
5
|
+
# Checks for an `initialize` that only assigns each of its arguments to an instance variable of the same name.
|
|
6
|
+
# attr_extras can define that for you.
|
|
7
|
+
#
|
|
8
|
+
# Positional defaults, splats and block arguments are accepted, since attr_extras has no equivalent. So are
|
|
9
|
+
# keyword defaults other than immutable literals: attr_extras evaluates them once, when the class is loaded.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# def initialize(user, notify:)
|
|
14
|
+
# @user = user
|
|
15
|
+
# @notify = notify
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# pattr_initialize :user, [ :notify! ]
|
|
20
|
+
class AttrExtrasInitialize < Base
|
|
21
|
+
MSG = "Use attr_extras, e.g. `pattr_initialize`, instead of an `initialize` that only assigns its arguments."
|
|
22
|
+
SUPPORTED_ARGUMENT_TYPES = %i[arg kwarg kwoptarg].freeze
|
|
23
|
+
IMMUTABLE_DEFAULT_TYPES = %i[nil true false sym int float].freeze
|
|
24
|
+
|
|
25
|
+
requires_gem "attr_extras"
|
|
26
|
+
|
|
27
|
+
def on_def(node)
|
|
28
|
+
return unless node.method?(:initialize)
|
|
29
|
+
return unless node.each_ancestor(:class, :module, :sclass).first&.class_type?
|
|
30
|
+
return unless supported_arguments?(node.arguments)
|
|
31
|
+
return unless only_assigns?(node.body, node.arguments.map(&:name))
|
|
32
|
+
|
|
33
|
+
add_offense(node.loc.keyword.join(node.loc.name))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def supported_arguments?(arguments)
|
|
39
|
+
arguments.any? && arguments.all? { |argument|
|
|
40
|
+
SUPPORTED_ARGUMENT_TYPES.include?(argument.type) &&
|
|
41
|
+
(!argument.kwoptarg_type? || IMMUTABLE_DEFAULT_TYPES.include?(argument.default_value.type))
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def only_assigns?(body, names)
|
|
46
|
+
return false unless body
|
|
47
|
+
|
|
48
|
+
statements = body.begin_type? ? body.children : [ body ]
|
|
49
|
+
assigned = statements.map { |statement| assigned_argument(statement) }
|
|
50
|
+
|
|
51
|
+
assigned.all? && assigned.sort == names.sort
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def assigned_argument(statement)
|
|
55
|
+
return unless statement.ivasgn_type? && statement.expression&.lvar_type?
|
|
56
|
+
|
|
57
|
+
name = statement.expression.children.first
|
|
58
|
+
name if statement.name == :"@#{name}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module RuboCop
|
|
3
|
+
module Cop
|
|
4
|
+
module Barsoom
|
|
5
|
+
# Checks for `private_class_method` listing class methods by name, when they're defined with `def self.` in the
|
|
6
|
+
# same class body. Putting the modifier before each definition shows at a glance which class methods are private.
|
|
7
|
+
#
|
|
8
|
+
# Names not defined that way, e.g. `private_class_method :new`, are accepted. The offense is only corrected when
|
|
9
|
+
# every listed name is defined in the class body.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# def self.parse(value) = Time.iso8601(value)
|
|
14
|
+
#
|
|
15
|
+
# private_class_method :parse
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# private_class_method \
|
|
19
|
+
# def self.parse(value) = Time.iso8601(value)
|
|
20
|
+
class InlinePrivateClassMethod < Base
|
|
21
|
+
include RangeHelp
|
|
22
|
+
extend AutoCorrector
|
|
23
|
+
|
|
24
|
+
MSG = "Use `private_class_method \\` before each `def self.` instead of listing the methods by name."
|
|
25
|
+
RESTRICT_ON_SEND = %i[private_class_method].freeze
|
|
26
|
+
|
|
27
|
+
def on_send(node)
|
|
28
|
+
return if node.receiver || node.arguments.empty? || !node.arguments.all?(&:sym_type?)
|
|
29
|
+
|
|
30
|
+
definitions = class_method_definitions(node.parent)
|
|
31
|
+
names = node.arguments.map(&:value)
|
|
32
|
+
return if names.none? { |name| definitions.key?(name) }
|
|
33
|
+
|
|
34
|
+
add_offense(node) do |corrector|
|
|
35
|
+
next unless names.all? { |name| definitions.key?(name) }
|
|
36
|
+
|
|
37
|
+
names.each do |name|
|
|
38
|
+
definition = definitions.fetch(name)
|
|
39
|
+
corrector.insert_before(definition, "private_class_method \\\n#{" " * definition.loc.column}")
|
|
40
|
+
end
|
|
41
|
+
corrector.remove(removal_range(node))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def class_method_definitions(body)
|
|
48
|
+
return {} unless body&.begin_type?
|
|
49
|
+
|
|
50
|
+
body.each_child_node(:defs).select { |definition| definition.receiver.self_type? }.to_h { |definition| [ definition.method_name, definition ] }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The whole line, and the blank line above it, so no blank line is left before the class's `end`.
|
|
54
|
+
def removal_range(node)
|
|
55
|
+
range = range_by_whole_lines(node.source_range, include_final_newline: true)
|
|
56
|
+
previous_line = processed_source.lines[node.first_line - 2]
|
|
57
|
+
previous_line&.strip&.empty? ? range.with(begin_pos: range.begin_pos - previous_line.length - 1) : range
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/shared_rubocop.yml
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Configuration for the Rubocop code linting tool. See README.
|
|
2
2
|
|
|
3
|
+
# Our custom cops, in lib/rubocop/cop/barsoom.
|
|
4
|
+
require:
|
|
5
|
+
- barsoom_utils/rubocop
|
|
6
|
+
|
|
3
7
|
AllCops:
|
|
4
8
|
ActiveSupportExtensionsEnabled: true
|
|
5
9
|
DisabledByDefault: true
|
|
@@ -381,3 +385,12 @@ Gemspec/RequireMFA:
|
|
|
381
385
|
# https://docs.rubocop.org/rubocop/cops_naming.html#namingblockforwarding
|
|
382
386
|
Naming/BlockForwarding:
|
|
383
387
|
Enabled: true
|
|
388
|
+
|
|
389
|
+
# Allow e.g. `pattr_initialize :user` and disallow an `initialize` that only assigns `@user = user`.
|
|
390
|
+
# Only runs in projects that have attr_extras in their Gemfile.lock.
|
|
391
|
+
Barsoom/AttrExtrasInitialize:
|
|
392
|
+
Enabled: true
|
|
393
|
+
|
|
394
|
+
# Allow e.g. `private_class_method \` before `def self.parse` and disallow `private_class_method :parse` further down.
|
|
395
|
+
Barsoom/InlinePrivateClassMethod:
|
|
396
|
+
Enabled: true
|
data/shared_rubocop_rails.yml
CHANGED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "rubocop_helper"
|
|
3
|
+
|
|
4
|
+
RSpec.describe RuboCop::Cop::Barsoom::AttrExtrasInitialize, :config do
|
|
5
|
+
let(:gem_versions) { { "attr_extras" => "7.1.0" } }
|
|
6
|
+
|
|
7
|
+
it "registers an offense for an initialize that only assigns its positional arguments" do
|
|
8
|
+
expect_offense(<<~RUBY)
|
|
9
|
+
class Foo
|
|
10
|
+
def initialize(bar, baz)
|
|
11
|
+
^^^^^^^^^^^^^^ Use attr_extras, e.g. `pattr_initialize`, instead of an `initialize` that only assigns its arguments.
|
|
12
|
+
@bar = bar
|
|
13
|
+
@baz = baz
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
RUBY
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "registers an offense for required keyword arguments" do
|
|
20
|
+
expect_offense(<<~RUBY)
|
|
21
|
+
class Foo
|
|
22
|
+
def initialize(bar, baz:)
|
|
23
|
+
^^^^^^^^^^^^^^ Use attr_extras, e.g. `pattr_initialize`, instead of an `initialize` that only assigns its arguments.
|
|
24
|
+
@bar = bar
|
|
25
|
+
@baz = baz
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
RUBY
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "registers an offense for keyword arguments with immutable literal defaults" do
|
|
32
|
+
expect_offense(<<~RUBY)
|
|
33
|
+
class Foo
|
|
34
|
+
def initialize(bar: nil, baz: :qux, limit: 10)
|
|
35
|
+
^^^^^^^^^^^^^^ Use attr_extras, e.g. `pattr_initialize`, instead of an `initialize` that only assigns its arguments.
|
|
36
|
+
@bar = bar
|
|
37
|
+
@baz = baz
|
|
38
|
+
@limit = limit
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
RUBY
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "accepts keyword defaults that attr_extras would evaluate once, at load time" do
|
|
45
|
+
expect_no_offenses(<<~RUBY)
|
|
46
|
+
class Foo
|
|
47
|
+
def initialize(now: Time.now, list: [], name: "x")
|
|
48
|
+
@now = now
|
|
49
|
+
@list = list
|
|
50
|
+
@name = name
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
RUBY
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "accepts positional defaults, which attr_extras doesn't support" do
|
|
57
|
+
expect_no_offenses(<<~RUBY)
|
|
58
|
+
class Foo
|
|
59
|
+
def initialize(bar, baz = nil)
|
|
60
|
+
@bar = bar
|
|
61
|
+
@baz = baz
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
RUBY
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "accepts splat and block arguments" do
|
|
68
|
+
expect_no_offenses(<<~RUBY)
|
|
69
|
+
class Foo
|
|
70
|
+
def initialize(*bars, **opts, &block)
|
|
71
|
+
@bars = bars
|
|
72
|
+
@opts = opts
|
|
73
|
+
@block = block
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
RUBY
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "accepts an initialize that does anything besides assigning its arguments" do
|
|
80
|
+
expect_no_offenses(<<~RUBY)
|
|
81
|
+
class Foo
|
|
82
|
+
def initialize(bar, baz)
|
|
83
|
+
@bar = bar
|
|
84
|
+
@baz = baz.to_s
|
|
85
|
+
@qux = []
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
RUBY
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "accepts an initialize that calls super" do
|
|
92
|
+
expect_no_offenses(<<~RUBY)
|
|
93
|
+
class Foo < Base
|
|
94
|
+
def initialize(bar)
|
|
95
|
+
super()
|
|
96
|
+
@bar = bar
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
RUBY
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "accepts instance variables named differently from the arguments" do
|
|
103
|
+
expect_no_offenses(<<~RUBY)
|
|
104
|
+
class Foo
|
|
105
|
+
def initialize(bar)
|
|
106
|
+
@baz = bar
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
RUBY
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "accepts an initialize that leaves an argument unassigned" do
|
|
113
|
+
expect_no_offenses(<<~RUBY)
|
|
114
|
+
class Foo
|
|
115
|
+
def initialize(bar, _baz)
|
|
116
|
+
@bar = bar
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
RUBY
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "accepts an initialize without arguments" do
|
|
123
|
+
expect_no_offenses(<<~RUBY)
|
|
124
|
+
class Foo
|
|
125
|
+
def initialize
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
RUBY
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "accepts an initialize in a module" do
|
|
132
|
+
expect_no_offenses(<<~RUBY)
|
|
133
|
+
module Foo
|
|
134
|
+
def initialize(bar)
|
|
135
|
+
@bar = bar
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
RUBY
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context "when the project doesn't use attr_extras" do
|
|
142
|
+
let(:gem_versions) { {} }
|
|
143
|
+
|
|
144
|
+
it "accepts the initialize" do
|
|
145
|
+
expect_no_offenses(<<~RUBY)
|
|
146
|
+
class Foo
|
|
147
|
+
def initialize(bar)
|
|
148
|
+
@bar = bar
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
RUBY
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "rubocop_helper"
|
|
3
|
+
|
|
4
|
+
RSpec.describe RuboCop::Cop::Barsoom::InlinePrivateClassMethod, :config do
|
|
5
|
+
let(:ruby_version) { 3.4 }
|
|
6
|
+
|
|
7
|
+
it "registers an offense for listing class methods by name, and moves the modifier onto each definition" do
|
|
8
|
+
expect_offense(<<~RUBY)
|
|
9
|
+
class Foo
|
|
10
|
+
def self.call = parse
|
|
11
|
+
|
|
12
|
+
def self.parse
|
|
13
|
+
:parsed
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.verifier = :verifier
|
|
17
|
+
|
|
18
|
+
private_class_method :parse, :verifier
|
|
19
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `private_class_method \\` before each `def self.` instead of listing the methods by name.
|
|
20
|
+
end
|
|
21
|
+
RUBY
|
|
22
|
+
|
|
23
|
+
expect_correction(<<~RUBY)
|
|
24
|
+
class Foo
|
|
25
|
+
def self.call = parse
|
|
26
|
+
|
|
27
|
+
private_class_method \\
|
|
28
|
+
def self.parse
|
|
29
|
+
:parsed
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private_class_method \\
|
|
33
|
+
def self.verifier = :verifier
|
|
34
|
+
end
|
|
35
|
+
RUBY
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "registers an offense without correcting it when some of the names aren't defined in the class body" do
|
|
39
|
+
expect_offense(<<~RUBY)
|
|
40
|
+
class Foo
|
|
41
|
+
def self.build = new
|
|
42
|
+
|
|
43
|
+
def self.parse = :parsed
|
|
44
|
+
|
|
45
|
+
private_class_method :new, :parse
|
|
46
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `private_class_method \\` before each `def self.` instead of listing the methods by name.
|
|
47
|
+
end
|
|
48
|
+
RUBY
|
|
49
|
+
|
|
50
|
+
expect_no_corrections
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "accepts the modifier before the definition" do
|
|
54
|
+
expect_no_offenses(<<~RUBY)
|
|
55
|
+
class Foo
|
|
56
|
+
private_class_method \\
|
|
57
|
+
def self.parse = :parsed
|
|
58
|
+
|
|
59
|
+
private_class_method def self.verifier = :verifier
|
|
60
|
+
end
|
|
61
|
+
RUBY
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "accepts names that aren't defined with `def self.` in the class body" do
|
|
65
|
+
expect_no_offenses(<<~RUBY)
|
|
66
|
+
class Foo
|
|
67
|
+
def self.build = new
|
|
68
|
+
|
|
69
|
+
private_class_method :new
|
|
70
|
+
end
|
|
71
|
+
RUBY
|
|
72
|
+
end
|
|
73
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: barsoom_utils
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.0.
|
|
4
|
+
version: 0.3.0.82
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tomas Skogberg
|
|
@@ -29,13 +29,19 @@ files:
|
|
|
29
29
|
- lib/barsoom_utils.rb
|
|
30
30
|
- lib/barsoom_utils/exception_notifier.rb
|
|
31
31
|
- lib/barsoom_utils/feature_toggle.rb
|
|
32
|
+
- lib/barsoom_utils/rubocop.rb
|
|
32
33
|
- lib/barsoom_utils/spec/debug_helpers.rb
|
|
33
34
|
- lib/barsoom_utils/version.rb
|
|
35
|
+
- lib/rubocop/cop/barsoom/attr_extras_initialize.rb
|
|
36
|
+
- lib/rubocop/cop/barsoom/inline_private_class_method.rb
|
|
34
37
|
- script/test
|
|
35
38
|
- shared_rubocop.yml
|
|
36
39
|
- shared_rubocop_rails.yml
|
|
37
40
|
- spec/exception_notifier_spec.rb
|
|
38
41
|
- spec/feature_toggle_spec.rb
|
|
42
|
+
- spec/rubocop/cop/barsoom/attr_extras_initialize_spec.rb
|
|
43
|
+
- spec/rubocop/cop/barsoom/inline_private_class_method_spec.rb
|
|
44
|
+
- spec/rubocop_helper.rb
|
|
39
45
|
- spec/spec/debug_helpers_spec.rb
|
|
40
46
|
- spec/spec_helper.rb
|
|
41
47
|
homepage: https://dev.auctionet.com/
|