rspectre 0.0.4 → 0.2.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: b0c337fb9cef9cb104d3ca686aa194cbb81062c0407e52ffdecec1e0c393d0fa
4
- data.tar.gz: 144eee36cdd52ba9f99d38b3bc7bb57bcbd5d283accc4ba0eef11e474fe99ed6
3
+ metadata.gz: 77d1fd05a8a9608c30ed29f93c408bf519507fe5e8560cb9a80542f13f2783fa
4
+ data.tar.gz: a2b683a3a2289c8f8b1e39fa09a0d3b55ca6e5d9337d922c8a0124b821d59242
5
5
  SHA512:
6
- metadata.gz: 1a5cd59d5dff5c9becdfc36c28e1605f7a3e222fe7ffa7543974ad6e80bb80238adb654140d3b976c910d5fda36e946456539720801dfdc72844c3c59040c8cf
7
- data.tar.gz: ea374c050f039f1c027d12586f091c2fd15731b3a62648a95c15153bca1c03f145357ad3162eed228e3cedec293af7ba2009fc4178bf23d506bae33aa485f3c4
6
+ metadata.gz: d8042b9d677bc5fbbd8ef98bac37f271a244622782391dda4926b0c63b5c5b21e36cdfa08e24e94b98a29079d5525180f5129e0534114fbe946538d1160ab679
7
+ data.tar.gz: 9e5bd54c5c3faedddaebb97a3265163ece4fece63d884ac22ba412dfe8c78de1e31cb097e83d3d0047ca10043908f74865e6c0c20b780c9e3fd0aa8f76cf546c
data/bin/rspectre CHANGED
@@ -20,8 +20,8 @@ OptionParser.new do |opts|
20
20
  opts.on(
21
21
  '--auto-correct',
22
22
  'Enables auto-correct.',
23
- 'When auto-correct is enabled, rspectre will modify your source files in place and delete any'\
24
- ' unused test setup.'
23
+ 'When auto-correct is enabled, rspectre will modify your source files in place and delete ' \
24
+ 'any unused test setup.'
25
25
  ) do |value|
26
26
  options[:'auto-correct'] = value
27
27
  end
@@ -29,4 +29,4 @@ end.parse(ARGV)
29
29
 
30
30
  rspec, auto_correct = options.fetch_values(:rspec, :'auto-correct')
31
31
 
32
- RSpectre::Runner.new(rspec, auto_correct).lint
32
+ RSpectre::Runner.new(rspec_arguments: rspec, auto_correct: auto_correct).lint
@@ -2,19 +2,19 @@
2
2
 
3
3
  module RSpectre
4
4
  class AutoCorrector < Parser::TreeRewriter
5
- include Concord.new(:filename, :nodes, :buffer)
5
+ include KeywordStruct.new(:filename, :nodes, :buffer)
6
6
 
7
7
  def initialize(filename, nodes)
8
8
  buffer = Parser::Source::Buffer.new("(#{filename})")
9
9
  buffer.source = File.read(filename)
10
10
 
11
- super(filename, nodes, buffer)
11
+ super(filename: filename, nodes: nodes, buffer: buffer)
12
12
  end
13
13
 
14
14
  def correct
15
15
  File.write(
16
16
  filename,
17
- rewrite(buffer, Parser::CurrentRuby.new.parse(buffer))
17
+ rewrite(buffer, SourceMap::Parser.parser_class.new.parse(buffer))
18
18
  )
19
19
  end
20
20
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpectre
4
+ # Heavily influenced by dkubb/equalizer, mbj/concord, and mbj/anima
5
+ class KeywordStruct < Module
6
+ def initialize(*names) # rubocop:disable Lint/MissingSuper
7
+ raise 'Specify at least one keyword name!' if names.empty?
8
+
9
+ names.each do |name|
10
+ next if /\A\w+\z/.match?(name)
11
+
12
+ raise "Invalid keyword name #{name.inspect}!"
13
+ end
14
+
15
+ @module =
16
+ Module.new do
17
+ attr_reader(*names)
18
+
19
+ define_method(:eql?) do |other|
20
+ other.instance_of?(self.class) && names.all? do |name|
21
+ __send__(name).eql?(other.__send__(name))
22
+ end
23
+ end
24
+ alias_method :==, :eql?
25
+
26
+ define_method(:hash) do
27
+ [self.class, *names.map { |name| __send__(name) }].hash
28
+ end
29
+
30
+ define_method(:inspect) do
31
+ class_name = self.class.name || self.class.inspect
32
+ attributes = names.map { |name| "#{name}=#{__send__(name).inspect}" }.join(' ')
33
+ "#<#{class_name} #{attributes}>"
34
+ end
35
+ end.tap do |generated_module|
36
+ generated_module.class_eval(<<~RUBY, __FILE__, __LINE__ + 1) # rubocop:disable Style/DocumentDynamicEvalDefinition
37
+ def initialize(#{names.map { |name| "#{name}:" }.join(', ')})
38
+ #{names.map { |name| "@#{name} = #{name}" }.join("\n ")}
39
+ end
40
+ RUBY
41
+ end
42
+ end
43
+
44
+ def included(descendant)
45
+ descendant.include(@module)
46
+ end
47
+ end
48
+ end
@@ -5,8 +5,8 @@ module RSpectre
5
5
  class UnusedLet < self
6
6
  TAG = 'UnusedLet'
7
7
 
8
- def example_group.let(name, &block)
9
- super(name, &block)
8
+ def example_group.let(name, &)
9
+ super
10
10
 
11
11
  UnusedLet.register(:let, caller_locations) do |node|
12
12
  UnusedLet.prepend_behavior(self, name) { UnusedLet.record(node) }
@@ -13,14 +13,14 @@ module RSpectre
13
13
  receiver.__send__(:define_singleton_method, method) do |name, *args, &block|
14
14
  # When we can locate the source of the node, tag it
15
15
  if (node = UnusedSharedSetup.register(method, caller_locations))
16
- # And call the orignal
16
+ # And call the original
17
17
  original_method.(name, *args) do |*shared_args|
18
18
  # But record that it was used in a `before`
19
19
  before { UnusedSharedSetup.record(node) }
20
20
 
21
21
  # And then perform the original block in a `class_exec` like the original block was
22
22
  # supposed to be
23
- class_exec(*shared_args, &block)
23
+ RSpec::Support::WithKeywordsWhenNeeded.class_exec(self, *shared_args, &block)
24
24
  end
25
25
  else
26
26
  # If we couldn't locate the source, just delegate to the original method.
@@ -48,37 +48,37 @@ module RSpectre
48
48
  # for now.
49
49
  def example_group.shared_examples(name, *args, &block)
50
50
  if (node = UnusedSharedSetup.register(:shared_examples, caller_locations))
51
- super(name, *args) do |*shared_args|
51
+ super do |*shared_args|
52
52
  before { UnusedSharedSetup.record(node) }
53
53
 
54
- class_exec(*shared_args, &block)
54
+ RSpec::Support::WithKeywordsWhenNeeded.class_exec(self, *shared_args, &block)
55
55
  end
56
56
  else
57
- super(name, *args, &block)
57
+ super
58
58
  end
59
59
  end
60
60
 
61
61
  def example_group.shared_examples_for(name, *args, &block)
62
62
  if (node = UnusedSharedSetup.register(:shared_examples_for, caller_locations))
63
- super(name, *args) do |*shared_args|
63
+ super do |*shared_args|
64
64
  before { UnusedSharedSetup.record(node) }
65
65
 
66
- class_exec(*shared_args, &block)
66
+ RSpec::Support::WithKeywordsWhenNeeded.class_exec(self, *shared_args, &block)
67
67
  end
68
68
  else
69
- super(name, *args, &block)
69
+ super
70
70
  end
71
71
  end
72
72
 
73
73
  def example_group.shared_context(name, *args, &block)
74
74
  if (node = UnusedSharedSetup.register(:shared_context, caller_locations))
75
- super(name, *args) do |*shared_args|
75
+ super do |*shared_args|
76
76
  before { UnusedSharedSetup.record(node) }
77
77
 
78
- class_exec(*shared_args, &block)
78
+ RSpec::Support::WithKeywordsWhenNeeded.class_exec(self, *shared_args, &block)
79
79
  end
80
80
  else
81
- super(name, *args, &block)
81
+ super
82
82
  end
83
83
  end
84
84
  end
@@ -5,8 +5,8 @@ module RSpectre
5
5
  class UnusedSubject < self
6
6
  TAG = 'UnusedSubject'
7
7
 
8
- def example_group.subject(name = nil, &block)
9
- super(*name, &block)
8
+ def example_group.subject(name = nil, &)
9
+ super(*name, &)
10
10
 
11
11
  UnusedSubject.register(:subject, caller_locations) do |node|
12
12
  UnusedSubject.prepend_behavior(self, :subject) { UnusedSubject.record(node) }
@@ -19,7 +19,7 @@ module RSpectre
19
19
  raw_node = node_map(file).find_method(selector, line)
20
20
 
21
21
  if raw_node
22
- node = RSpectre::Node.new(file, line, raw_node)
22
+ node = RSpectre::Node.new(file: file, line: line, node: raw_node)
23
23
  TRACKER.register(self::TAG, node)
24
24
  if block_given?
25
25
  yield node
@@ -40,10 +40,12 @@ module RSpectre
40
40
  def self.prepend_behavior(scope, method_name)
41
41
  original_method = scope.instance_method(method_name)
42
42
 
43
- scope.__send__(:define_method, method_name) do |*args, &block|
43
+ # Removing the method first prevents method redefined warnings when $VERBOSE is true
44
+ scope.remove_method(method_name)
45
+ scope.__send__(:define_method, method_name) do |*args, **kwargs, &block|
44
46
  yield
45
47
 
46
- original_method.bind(self).(*args, &block)
48
+ original_method.bind_call(self, *args, **kwargs, &block)
47
49
  end
48
50
  end
49
51
 
data/lib/rspectre/node.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RSpectre
4
4
  class Node
5
- include Concord::Public.new(:file, :line, :node)
5
+ include KeywordStruct.new(:file, :line, :node)
6
6
 
7
7
  def start_column
8
8
  location.column + 1
@@ -27,7 +27,11 @@ module RSpectre
27
27
  end
28
28
 
29
29
  def location
30
- node.children.first.location
30
+ if node.type.equal?(:block)
31
+ node.children.first.location
32
+ else
33
+ node.location
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  module RSpectre
4
4
  class Offense
5
- include Anima.new(:file, :line, :source_line, :start_column, :end_column, :type)
5
+ include KeywordStruct.new(:file, :line, :source_line, :start_column, :end_column, :type)
6
6
 
7
7
  DESCRIPTIONS = {
8
8
  'UnusedLet' => 'Unused `let` definition.',
9
9
  'UnusedSubject' => 'Unused `subject` definition.',
10
- 'UnusedSharedSetup' => 'Unused `shared_examples`, `shared_examples_for`, or'\
11
- ' `shared_context` definition.'
10
+ 'UnusedSharedSetup' => 'Unused `shared_examples`, `shared_examples_for`, or ' \
11
+ '`shared_context` definition.'
12
12
  }.freeze
13
13
 
14
14
  def self.parse(type, node)
@@ -23,7 +23,7 @@ module RSpectre
23
23
  end
24
24
 
25
25
  def warn
26
- puts to_s
26
+ puts self
27
27
  end
28
28
 
29
29
  def to_s
@@ -2,11 +2,11 @@
2
2
 
3
3
  module RSpectre
4
4
  class Runner
5
- include Concord.new(:rspec_arguments, :auto_correct)
5
+ include KeywordStruct.new(:rspec_arguments, :auto_correct)
6
6
 
7
7
  EXIT_SUCCESS = 0
8
8
 
9
- def initialize(*)
9
+ def initialize(**)
10
10
  super
11
11
  @rspec_output = StringIO.new
12
12
  end
@@ -39,7 +39,7 @@ module RSpectre
39
39
 
40
40
  abort(
41
41
  Color.red(
42
- 'Running the specs failed. Either your tests do not pass '\
42
+ 'Running the specs failed. Either your tests do not pass ' \
43
43
  'normally or this is a bug in RSpectre.'
44
44
  ) + <<~TEXT
45
45
 
@@ -3,7 +3,7 @@
3
3
  module RSpectre
4
4
  class SourceMap
5
5
  class Parser
6
- include Concord.new(:file)
6
+ include KeywordStruct.new(:file)
7
7
 
8
8
  def populate(map)
9
9
  walk(parsed_source) { |node| map.add(node) }
@@ -15,6 +15,14 @@ module RSpectre
15
15
  Null.new
16
16
  end
17
17
 
18
+ def self.parser_class
19
+ if RSpectre.ruby_version_supports_prism?
20
+ Prism::Translation::Parser
21
+ else
22
+ ::Parser::CurrentRuby
23
+ end
24
+ end
25
+
18
26
  private
19
27
 
20
28
  def walk(node, &block)
@@ -28,9 +36,8 @@ module RSpectre
28
36
  end
29
37
 
30
38
  def parsed_source
31
- parser = ::Parser::CurrentRuby.new(PermissiveASTBuilder.new)
39
+ parser = self.class.parser_class.new(PermissiveASTBuilder.new)
32
40
  buffer = ::Parser::Source::Buffer.new(file, source: raw_source)
33
-
34
41
  parser.parse(buffer)
35
42
  end
36
43
 
@@ -2,15 +2,15 @@
2
2
 
3
3
  module RSpectre
4
4
  class SourceMap
5
- include Concord.new(:map)
5
+ include KeywordStruct.new(:map)
6
6
 
7
7
  def initialize
8
- super(Hash.new { [] })
8
+ super(map: Hash.new { [] })
9
9
  end
10
10
  private_class_method :new
11
11
 
12
12
  def self.parse(file)
13
- self::Parser.new(file).populate(new)
13
+ self::Parser.new(file: file).populate(new)
14
14
  end
15
15
 
16
16
  def add(node)
@@ -32,18 +32,35 @@ module RSpectre
32
32
  private
33
33
 
34
34
  def find_methods(target_selector, line)
35
- block_nodes(line).select do |node|
36
- send, = *node
37
- _receiver, selector = *send
35
+ block_candidates =
36
+ block_nodes(line).select do |node|
37
+ send, = *node
38
+ matching_send?(send, target_selector)
39
+ end
38
40
 
39
- selector.equal?(target_selector)
41
+ if block_candidates.any?
42
+ block_candidates
43
+ else
44
+ send_nodes(line).select do |node|
45
+ matching_send?(node, target_selector)
46
+ end
40
47
  end
41
48
  end
42
49
 
50
+ def matching_send?(node, method_name)
51
+ _receiver, selector = *node
52
+
53
+ selector.equal?(method_name)
54
+ end
55
+
43
56
  def block_nodes(line)
44
57
  map.fetch(line, []).select { |node| node.type.equal?(:block) }
45
58
  end
46
59
 
60
+ def send_nodes(line)
61
+ map.fetch(line, []).select { |node| node.type.equal?(:send) }
62
+ end
63
+
47
64
  class Null < self
48
65
  public_class_method :new
49
66
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  module RSpectre
4
4
  class Tracker
5
- include Concord.new(:registry, :tracker)
5
+ include KeywordStruct.new(:registry, :tracker)
6
6
 
7
7
  def initialize
8
- super(Hash.new { Set.new }, Hash.new { Set.new })
8
+ super(registry: Hash.new { Set.new }, tracker: Hash.new { Set.new })
9
9
  end
10
10
 
11
11
  def register(type, node)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpectre
4
- VERSION = '0.0.4'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/rspectre.rb CHANGED
@@ -6,10 +6,26 @@ require 'pathname'
6
6
  require 'set'
7
7
  require 'stringio'
8
8
 
9
- require 'anima'
10
- require 'concord'
11
- require 'parser/current'
12
9
  require 'rspec'
10
+ module RSpectre
11
+ LIB_PATH = Pathname.new(File.expand_path('../lib', __dir__))
12
+
13
+ MINIMUM_RUBY_FOR_PRISM = Gem::Version.new('3.3')
14
+ private_constant(:MINIMUM_RUBY_FOR_PRISM)
15
+
16
+ def self.ruby_version_supports_prism?
17
+ Gem::Version.new(RUBY_VERSION) >= MINIMUM_RUBY_FOR_PRISM
18
+ end
19
+ end
20
+
21
+ if RSpectre.ruby_version_supports_prism?
22
+ require 'parser'
23
+ require 'prism'
24
+ else
25
+ require 'parser/current'
26
+ end
27
+
28
+ require 'rspectre/keyword_struct'
13
29
 
14
30
  require 'rspectre/auto_corrector'
15
31
  require 'rspectre/color'
@@ -28,5 +44,4 @@ require 'rspectre/linter/unused_shared_setup'
28
44
 
29
45
  module RSpectre
30
46
  TRACKER = Tracker.new
31
- LIB_PATH = Pathname.new(File.expand_path('../lib', __dir__))
32
47
  end
metadata CHANGED
@@ -1,115 +1,84 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspectre
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Gollahon
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-01-10 00:00:00.000000000 Z
10
+ date: 2025-03-17 00:00:00.000000000 Z
12
11
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: anima
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.3'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '0.3'
27
- - !ruby/object:Gem::Dependency
28
- name: concord
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.1'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.1'
41
12
  - !ruby/object:Gem::Dependency
42
13
  name: parser
43
14
  requirement: !ruby/object:Gem::Requirement
44
15
  requirements:
45
16
  - - ">="
46
17
  - !ruby/object:Gem::Version
47
- version: '2.6'
18
+ version: 3.3.7.1
48
19
  type: :runtime
49
20
  prerelease: false
50
21
  version_requirements: !ruby/object:Gem::Requirement
51
22
  requirements:
52
23
  - - ">="
53
24
  - !ruby/object:Gem::Version
54
- version: '2.6'
25
+ version: 3.3.7.1
55
26
  - !ruby/object:Gem::Dependency
56
- name: rspec
27
+ name: prism
57
28
  requirement: !ruby/object:Gem::Requirement
58
29
  requirements:
59
30
  - - "~>"
60
31
  - !ruby/object:Gem::Version
61
- version: '3.0'
32
+ version: '1.3'
62
33
  type: :runtime
63
34
  prerelease: false
64
35
  version_requirements: !ruby/object:Gem::Requirement
65
36
  requirements:
66
37
  - - "~>"
67
38
  - !ruby/object:Gem::Version
68
- version: '3.0'
39
+ version: '1.3'
69
40
  - !ruby/object:Gem::Dependency
70
- name: pry
41
+ name: rspec
71
42
  requirement: !ruby/object:Gem::Requirement
72
43
  requirements:
73
44
  - - "~>"
74
45
  - !ruby/object:Gem::Version
75
- version: '0.14'
76
- type: :development
46
+ version: '3.10'
47
+ type: :runtime
77
48
  prerelease: false
78
49
  version_requirements: !ruby/object:Gem::Requirement
79
50
  requirements:
80
51
  - - "~>"
81
52
  - !ruby/object:Gem::Version
82
- version: '0.14'
53
+ version: '3.10'
83
54
  - !ruby/object:Gem::Dependency
84
55
  name: rubocop
85
56
  requirement: !ruby/object:Gem::Requirement
86
57
  requirements:
87
58
  - - "~>"
88
59
  - !ruby/object:Gem::Version
89
- version: 1.24.1
60
+ version: 1.71.2
90
61
  type: :development
91
62
  prerelease: false
92
63
  version_requirements: !ruby/object:Gem::Requirement
93
64
  requirements:
94
65
  - - "~>"
95
66
  - !ruby/object:Gem::Version
96
- version: 1.24.1
67
+ version: 1.71.2
97
68
  - !ruby/object:Gem::Dependency
98
69
  name: rubocop-rspec
99
70
  requirement: !ruby/object:Gem::Requirement
100
71
  requirements:
101
72
  - - "~>"
102
73
  - !ruby/object:Gem::Version
103
- version: 2.7.0
74
+ version: 3.4.0
104
75
  type: :development
105
76
  prerelease: false
106
77
  version_requirements: !ruby/object:Gem::Requirement
107
78
  requirements:
108
79
  - - "~>"
109
80
  - !ruby/object:Gem::Version
110
- version: 2.7.0
111
- description:
112
- email:
81
+ version: 3.4.0
113
82
  executables:
114
83
  - rspectre
115
84
  extensions: []
@@ -119,6 +88,7 @@ files:
119
88
  - lib/rspectre.rb
120
89
  - lib/rspectre/auto_corrector.rb
121
90
  - lib/rspectre/color.rb
91
+ - lib/rspectre/keyword_struct.rb
122
92
  - lib/rspectre/linter.rb
123
93
  - lib/rspectre/linter/unused_let.rb
124
94
  - lib/rspectre/linter/unused_shared_setup.rb
@@ -136,7 +106,6 @@ licenses:
136
106
  - MIT
137
107
  metadata:
138
108
  rubygems_mfa_required: 'true'
139
- post_install_message:
140
109
  rdoc_options: []
141
110
  require_paths:
142
111
  - lib
@@ -144,15 +113,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
113
  requirements:
145
114
  - - ">="
146
115
  - !ruby/object:Gem::Version
147
- version: '2.6'
116
+ version: '3.1'
148
117
  required_rubygems_version: !ruby/object:Gem::Requirement
149
118
  requirements:
150
119
  - - ">="
151
120
  - !ruby/object:Gem::Version
152
121
  version: '0'
153
122
  requirements: []
154
- rubygems_version: 3.1.6
155
- signing_key:
123
+ rubygems_version: 3.6.2
156
124
  specification_version: 4
157
125
  summary: A tool for linting RSpec test suites.
158
126
  test_files: []