leftovers 0.2.3 → 0.3.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.
@@ -3,6 +3,14 @@
3
3
  module Leftovers
4
4
  ruby_version = Gem::Version.new(RUBY_VERSION)
5
5
  unless ruby_version >= Gem::Version.new('2.5')
6
+ require 'set'
7
+ module SetCaseEq
8
+ refine ::Set do
9
+ def ===(value)
10
+ include?(value)
11
+ end
12
+ end
13
+ end
6
14
  module StringDeletePrefixSuffix
7
15
  refine ::String do
8
16
  def delete_prefix!(str)
@@ -24,33 +32,5 @@ module Leftovers
24
32
  end
25
33
  end
26
34
  end
27
-
28
- require 'yaml'
29
- module YAMLSymbolizeNames
30
- refine YAML.singleton_class do
31
- alias_method :safe_load_without_symbolize_names, :safe_load
32
- def safe_load(path, *args, symbolize_names: false, **kwargs)
33
- if symbolize_names
34
- symbolize_names!(safe_load_without_symbolize_names(path, *args, **kwargs))
35
- else
36
- safe_load_without_symbolize_names(path, *args, **kwargs)
37
- end
38
- end
39
-
40
- private
41
-
42
- def symbolize_names!(obj) # rubocop:disable Metrics/MethodLength
43
- case obj
44
- when Hash
45
- obj.keys.each do |key| # rubocop:disable Style/HashEachMethods # each_key never finishes.
46
- obj[key.to_sym] = symbolize_names!(obj.delete(key))
47
- end
48
- when Array
49
- obj.map! { |ea| symbolize_names!(ea) }
50
- end
51
- obj
52
- end
53
- end
54
- end
55
35
  end
56
36
  end
@@ -6,25 +6,31 @@ require_relative 'version'
6
6
 
7
7
  module Leftovers
8
8
  class CLI
9
- attr_reader :argv, :stdout, :stderr
10
-
11
9
  def initialize(argv: [], stdout: $stdout, stderr: $stderr)
12
10
  @argv = argv
13
11
  @stdout = stdout
14
12
  @stderr = stderr
13
+ end
15
14
 
16
- parse_options
15
+ def run
16
+ catch(:leftovers_exit) do
17
+ Leftovers.reset
18
+ parse_options
17
19
 
18
- exit Leftovers.run(stdout: stdout, stderr: stderr)
20
+ Leftovers.run(stdout: stdout, stderr: stderr)
21
+ end
19
22
  end
20
23
 
24
+ private
25
+
26
+ attr_reader :argv, :stdout, :stderr
27
+
21
28
  def parse_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
22
29
  opts = OptionParser.new
23
30
  Leftovers.parallel = true
24
31
  Leftovers.progress = true
25
32
 
26
33
  opts.banner = 'Usage: leftovers [options]'
27
- opts.on('-q', '--quiet', 'Silences output') { Leftovers.quiet = true }
28
34
  opts.on('--[no-]parallel', 'Run in parallel or not, default --parallel') do |p|
29
35
  Leftovers.parallel = p
30
36
  end
@@ -32,16 +38,16 @@ module Leftovers
32
38
  Leftovers.progress = p
33
39
  end
34
40
  opts.on('-v', '--version', 'Returns the current version') do
35
- stdout.puts(Leftovers::Version)
36
- exit
41
+ stdout.puts(Leftovers::VERSION)
42
+ Leftovers.exit
37
43
  end
38
44
  opts.on('--dry-run', 'Output files that will be looked at') do
39
- stdout.puts(Leftovers::FileList.new.to_a.map(&:relative_path).join("\n"))
40
- exit
45
+ Leftovers::FileList.new.each { |f| stdout.puts f.relative_path }
46
+ Leftovers.exit
41
47
  end
42
48
  opts.on('-h', '--help', 'Shows this message') do
43
49
  stdout.puts(opts.help)
44
- exit
50
+ Leftovers.exit
45
51
  end
46
52
 
47
53
  opts.parse(argv)
@@ -31,9 +31,9 @@ module Leftovers
31
31
 
32
32
  def collect_file_list(list)
33
33
  if Leftovers.parallel?
34
- Parallel.each(list, finish: method(:finish_parallel), &method(:collect_file))
34
+ Parallel.each(list, finish: method(:finish_file), &method(:collect_file))
35
35
  else
36
- list.each { |file| finish_parallel(nil, nil, collect_file(file)) }
36
+ list.each { |file| finish_file(nil, nil, collect_file(file)) }
37
37
  end
38
38
  end
39
39
 
@@ -46,11 +46,11 @@ module Leftovers
46
46
 
47
47
  def print_progress
48
48
  Leftovers.print(
49
- "checked #{@count} files, collected #{@count_calls} calls, #{@count_definitions} definitions\r" # rubocop:disable Layout/LineLength
49
+ "\e[2Kchecked #{@count} files, collected #{@count_calls} calls, #{@count_definitions} definitions\r" # rubocop:disable Layout/LineLength
50
50
  )
51
51
  end
52
52
 
53
- def finish_parallel(_, _, result) # rubocop:disable Metrics/MethodLength
53
+ def finish_file(_, _, result) # rubocop:disable Metrics/MethodLength
54
54
  @count += 1
55
55
  @count_calls += result[:calls].length
56
56
  @count_definitions += result[:definitions].length
@@ -6,16 +6,12 @@ require_relative 'rule'
6
6
  module Leftovers
7
7
  class Config
8
8
  # :nocov:
9
- using ::Leftovers::YAMLSymbolizeNames if defined?(::Leftovers::YAMLSymbolizeNames)
9
+ using ::Leftovers::SetCaseEq if defined?(::Leftovers::SetCaseEq)
10
10
  # :nocov:
11
11
 
12
12
  attr_reader :name
13
13
 
14
- def initialize(
15
- name,
16
- path: ::File.join(__dir__, '..', 'config', "#{name}.yml"),
17
- content: (::File.exist?(path) ? ::File.read(path) : '')
18
- )
14
+ def initialize(name, path: nil, content: nil)
19
15
  @name = name.to_sym
20
16
  @path = path
21
17
  @content = content
@@ -39,15 +35,46 @@ module Leftovers
39
35
 
40
36
  def rules
41
37
  @rules ||= Rule.wrap(yaml[:rules])
38
+ rescue Leftovers::ConfigError => e
39
+ warn "\e[31mConfig Error: (#{path}): #{e.message}\e[0m"
40
+ Leftovers.exit 1
42
41
  end
43
42
 
44
43
  private
45
44
 
46
- def yaml
47
- @yaml ||= YAML.safe_load(@content, symbolize_names: true) || {}
48
- rescue Psych::SyntaxError => e
49
- warn "\e[31mError with config #{path}: #{e.message}\e[0m"
50
- exit 1
45
+ def content
46
+ @content ||= ::File.exist?(path) ? ::File.read(path) : ''
51
47
  end
48
+
49
+ def path
50
+ @path ||= ::File.expand_path("../config/#{name}.yml", __dir__)
51
+ end
52
+
53
+ def yaml # rubocop:disable Metrics/MethodLength
54
+ # :nocov:
55
+ @yaml ||= if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
56
+ Psych.safe_load(content, symbolize_names: true, filename: path) || {}
57
+ else
58
+ symbolize_names!(Psych.safe_load(content, [], [], false, path)) || {}
59
+ end
60
+ # :nocov:
61
+ rescue ::Psych::SyntaxError => e
62
+ warn "\e[31mConfig SyntaxError: #{e.message}\e[0m"
63
+ Leftovers.exit 1
64
+ end
65
+
66
+ # :nocov:
67
+ def symbolize_names!(obj) # rubocop:disable Metrics/MethodLength
68
+ case obj
69
+ when Hash
70
+ obj.keys.each do |key| # rubocop:disable Style/HashEachMethods # each_key never finishes.
71
+ obj[key.to_sym] = symbolize_names!(obj.delete(key))
72
+ end
73
+ when Array
74
+ obj.map! { |ea| symbolize_names!(ea) }
75
+ end
76
+ obj
77
+ end
78
+ # :nocov:
52
79
  end
53
80
  end
@@ -3,11 +3,13 @@
3
3
  require 'set'
4
4
 
5
5
  class Array
6
- def leftovers_append(other)
7
- case other
8
- when Array, Set then concat(other)
9
- when nil then self
10
- else self.<< other
6
+ def leftovers_append(other) # rubocop:disable Metrics/MethodLength
7
+ return self if other.nil?
8
+
9
+ if other.respond_to?(:to_a)
10
+ concat(other.to_a)
11
+ else
12
+ self << other
11
13
  end
12
14
  end
13
15
  end
@@ -4,13 +4,10 @@ module Leftovers
4
4
  class Definition
5
5
  attr_reader :name
6
6
  alias_method :names, :name
7
- alias_method :full_name, :name
8
- attr_reader :name_s
9
- alias_method :to_s, :name_s
10
7
  attr_reader :test
11
8
  alias_method :test?, :test
12
9
 
13
- def initialize( # rubocop:disable Metrics/MethodLength
10
+ def initialize(
14
11
  name,
15
12
  method_node: nil,
16
13
  location: method_node.loc.expression,
@@ -18,7 +15,6 @@ module Leftovers
18
15
  test: method_node.test?
19
16
  )
20
17
  @name = name
21
- @name_s = name.to_s.freeze
22
18
 
23
19
  @location = location
24
20
  @file = file
@@ -37,6 +33,10 @@ module Leftovers
37
33
  @file.relative_path
38
34
  end
39
35
 
36
+ def to_s
37
+ @name.to_s
38
+ end
39
+
40
40
  def line
41
41
  @location.line
42
42
  end
@@ -46,7 +46,7 @@ module Leftovers
46
46
  end
47
47
 
48
48
  def full_location
49
- "#{path}:#{@location.line}:#{@location.column}"
49
+ "#{path}:#{line}:#{column}"
50
50
  end
51
51
 
52
52
  def highlighted_source(highlight = "\e[31m", normal = "\e[0m") # rubocop:disable Metrics/AbcSize
@@ -64,7 +64,7 @@ module Leftovers
64
64
  end
65
65
 
66
66
  def skipped?
67
- Leftovers.config.skip_rules.any? { |r| r.match?(@name, @name_s, path) }
67
+ Leftovers.config.skip_rules.any? { |r| r.match?(@name, path) }
68
68
  end
69
69
  end
70
70
  end
@@ -21,10 +21,6 @@ module Leftovers
21
21
  freeze
22
22
  end
23
23
 
24
- def full_name
25
- names.join(', ')
26
- end
27
-
28
24
  def names
29
25
  @definitions.map(&:names)
30
26
  end
@@ -19,7 +19,7 @@ module Leftovers
19
19
  def ruby # rubocop:disable Metrics/MethodLength
20
20
  case extname
21
21
  when '.haml'
22
- Leftovers::Haml.precompile(read)
22
+ Leftovers::Haml.precompile(read, self)
23
23
  when '.rhtml', '.rjs', '.erb'
24
24
  Leftovers::ERB.precompile(read)
25
25
  else
@@ -52,11 +52,9 @@ module Leftovers
52
52
  def process_comments(comments) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
53
53
  comments.each do |comment|
54
54
  @allow_lines << comment.loc.line if comment.text.match?(LEFTOVERS_ALLOW_RE)
55
-
56
55
  @test_lines << comment.loc.line if comment.text.match?(LEFTOVERS_TEST_RE)
57
56
 
58
57
  next unless (match = comment.text.match(LEFTOVERS_CALL_RE))
59
- next unless match[1]
60
58
 
61
59
  match[1].scan(NAME_RE).each { |s| add_call(s.to_sym) }
62
60
  end
@@ -64,13 +62,15 @@ module Leftovers
64
62
 
65
63
  # grab method definitions
66
64
  def on_def(node)
67
- add_definition(node.children.first, node.loc.name)
65
+ add_definition(node.name, node.loc.name)
68
66
 
69
67
  super
70
68
  end
71
69
 
72
70
  def on_ivasgn(node)
73
- add_definition(node.children.first, node.loc.name)
71
+ add_definition(node.name, node.loc.name)
72
+
73
+ collect_rules(node)
74
74
 
75
75
  super
76
76
  end
@@ -78,7 +78,7 @@ module Leftovers
78
78
  alias_method :on_cvasgn, :on_ivasgn
79
79
 
80
80
  def on_ivar(node)
81
- add_call(node.children.first)
81
+ add_call(node.name)
82
82
 
83
83
  super
84
84
  end
@@ -107,8 +107,7 @@ module Leftovers
107
107
  def on_send(node)
108
108
  super
109
109
 
110
- add_call(node.children[1])
111
-
110
+ add_call(node.name)
112
111
  collect_rules(node)
113
112
  end
114
113
  alias_method :on_csend, :on_send
@@ -116,14 +115,14 @@ module Leftovers
116
115
  def on_const(node)
117
116
  super
118
117
 
119
- add_call(node.children[1])
118
+ add_call(node.name)
120
119
  end
121
120
 
122
121
  # grab e.g. :to_s in each(&:to_s)
123
122
  def on_block_pass(node)
124
123
  super
125
124
 
126
- add_call(node.children.first.to_sym) if node.children.first&.string_or_symbol?
125
+ add_call(node.children.first.to_sym) if node.children.first.string_or_symbol?
127
126
  end
128
127
 
129
128
  # grab class Constant or module Constant
@@ -134,7 +133,7 @@ module Leftovers
134
133
 
135
134
  node = node.children.first
136
135
 
137
- add_definition(node.children[1], node.loc.name)
136
+ add_definition(node.name, node.loc.name)
138
137
  end
139
138
  alias_method :on_module, :on_class
140
139
 
@@ -142,7 +141,7 @@ module Leftovers
142
141
  def on_casgn(node)
143
142
  super
144
143
 
145
- add_definition(node.children[1], node.loc.name)
144
+ add_definition(node.name, node.loc.name)
146
145
 
147
146
  collect_rules(node)
148
147
  end
@@ -177,22 +176,20 @@ module Leftovers
177
176
  def collect_var_op_asgn(node)
178
177
  name = node.children.first
179
178
 
180
- return unless name
181
-
182
179
  add_call(name)
183
180
  end
184
181
 
185
182
  def collect_send_op_asgn(node)
186
183
  name = node.children[1]
187
184
 
188
- return unless name
189
-
190
185
  add_call(:"#{name}=")
191
186
  end
192
187
 
193
188
  def collect_op_asgn(node)
194
189
  node = node.children.first
190
+ # :nocov: # don't need else, it's exhaustive for callers
195
191
  case node.type
192
+ # :nocov:
196
193
  when :send then collect_send_op_asgn(node)
197
194
  when :ivasgn, :gvasgn, :cvasgn then collect_var_op_asgn(node)
198
195
  end
@@ -200,7 +197,7 @@ module Leftovers
200
197
 
201
198
  def collect_rules(node) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
202
199
  Leftovers.config.rules.each do |rule|
203
- next unless rule.match?(node.name, node.name_s, filename)
200
+ next unless rule.match?(node.name, filename)
204
201
 
205
202
  next if rule.skip?
206
203
 
@@ -4,24 +4,19 @@ require 'fast_ignore'
4
4
  require_relative 'file'
5
5
 
6
6
  module Leftovers
7
- class FileList
8
- include Enumerable
7
+ class FileList < ::FastIgnore
8
+ def initialize
9
+ super(
10
+ ignore_rules: Leftovers.config.exclude_paths,
11
+ include_rules: Leftovers.config.include_paths,
12
+ root: Leftovers.pwd
13
+ )
14
+ end
9
15
 
10
16
  def each
11
- fast_ignore.each do |file|
17
+ super do |file|
12
18
  yield(Leftovers::File.new(file))
13
19
  end
14
20
  end
15
-
16
- def to_a
17
- enum_for(:each).to_a
18
- end
19
-
20
- def fast_ignore
21
- FastIgnore.new(
22
- ignore_rules: Leftovers.config.exclude_paths,
23
- include_rules: ['#!:ruby'] + Leftovers.config.include_paths
24
- )
25
- end
26
21
  end
27
22
  end
@@ -4,19 +4,16 @@ module Leftovers
4
4
  module Haml
5
5
  module_function
6
6
 
7
- def precompile(file) # rubocop:disable Metrics/MethodLength
8
- Leftovers.try_require('haml', message: <<~MESSAGE)
9
- Skipped parsing a haml file, because the haml gem was not available
10
- `gem install Haml`
7
+ def precompile(file, name) # rubocop:disable Metrics/MethodLength
8
+ return '' unless Leftovers.try_require('haml', message: <<~MESSAGE) # rubocop:disable Layout/EmptyLineAfterGuardClause
9
+ Skipped parsing #{name.relative_path}, because the haml gem was not available
10
+ `gem install haml`
11
11
  MESSAGE
12
- if defined?(::Haml)
13
- begin
14
- ::Haml::Engine.new(file).precompiled
15
- rescue ::Haml::SyntaxError => e
16
- Leftovers.warn "#{e.class}: #{e.message} #{filename}:#{e.line}"
17
- ''
18
- end
19
- else
12
+
13
+ begin
14
+ ::Haml::Engine.new(file).precompiled
15
+ rescue ::Haml::SyntaxError => e
16
+ Leftovers.warn "#{e.class}: #{e.message} #{name.relative_path}:#{e.line}"
20
17
  ''
21
18
  end
22
19
  end