leftovers 0.1.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 +7 -0
- data/.gitignore +4 -0
- data/.leftovers.yml +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +216 -0
- data/.ruby-version +1 -0
- data/.spellr.yml +13 -0
- data/.spellr_wordlists/english.txt +21 -0
- data/.spellr_wordlists/lorem.txt +1 -0
- data/.spellr_wordlists/ruby.txt +67 -0
- data/.spellr_wordlists/shell.txt +3 -0
- data/.travis.yml +10 -0
- data/Configuration.md +427 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +172 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/leftovers +6 -0
- data/leftovers.gemspec +42 -0
- data/lib/config/attr_encrypted.yml +16 -0
- data/lib/config/builder.yml +3 -0
- data/lib/config/capistrano.yml +3 -0
- data/lib/config/datagrid.yml +9 -0
- data/lib/config/flipper.yml +3 -0
- data/lib/config/graphql.yml +20 -0
- data/lib/config/guard.yml +3 -0
- data/lib/config/haml.yml +2 -0
- data/lib/config/jbuilder.yml +2 -0
- data/lib/config/okcomputer.yml +3 -0
- data/lib/config/parser.yml +91 -0
- data/lib/config/pry.yml +3 -0
- data/lib/config/rack.yml +2 -0
- data/lib/config/rails.yml +387 -0
- data/lib/config/rake.yml +5 -0
- data/lib/config/redcarpet.yml +38 -0
- data/lib/config/rollbar.yml +3 -0
- data/lib/config/rspec.yml +56 -0
- data/lib/config/ruby.yml +77 -0
- data/lib/config/selenium.yml +21 -0
- data/lib/config/simplecov.yml +2 -0
- data/lib/config/will_paginate.yml +14 -0
- data/lib/leftovers/argument_rule.rb +216 -0
- data/lib/leftovers/backports.rb +56 -0
- data/lib/leftovers/cli.rb +50 -0
- data/lib/leftovers/collector.rb +67 -0
- data/lib/leftovers/config.rb +53 -0
- data/lib/leftovers/core_ext.rb +32 -0
- data/lib/leftovers/definition.rb +70 -0
- data/lib/leftovers/definition_set.rb +44 -0
- data/lib/leftovers/erb.rb +20 -0
- data/lib/leftovers/file.rb +30 -0
- data/lib/leftovers/file_collector.rb +219 -0
- data/lib/leftovers/file_list.rb +24 -0
- data/lib/leftovers/haml.rb +24 -0
- data/lib/leftovers/hash_rule.rb +40 -0
- data/lib/leftovers/merged_config.rb +71 -0
- data/lib/leftovers/name_rule.rb +53 -0
- data/lib/leftovers/node.rb +182 -0
- data/lib/leftovers/rake_task.rb +62 -0
- data/lib/leftovers/reporter.rb +11 -0
- data/lib/leftovers/rule.rb +74 -0
- data/lib/leftovers/transform_rule.rb +171 -0
- data/lib/leftovers/value_rule.rb +56 -0
- data/lib/leftovers/version.rb +5 -0
- data/lib/leftovers.rb +127 -0
- metadata +281 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
test_paths:
|
2
|
+
- /spec/
|
3
|
+
|
4
|
+
rules:
|
5
|
+
- skip: true
|
6
|
+
name:
|
7
|
+
# matchers
|
8
|
+
- diffable?
|
9
|
+
- actual
|
10
|
+
# formatters:
|
11
|
+
- start
|
12
|
+
- example_group_started
|
13
|
+
- example_started
|
14
|
+
- example_passed
|
15
|
+
- example_failed
|
16
|
+
- example_pending
|
17
|
+
- message
|
18
|
+
- stop
|
19
|
+
- start_dump
|
20
|
+
- dump_pending
|
21
|
+
- dump_failures
|
22
|
+
- dump_summary
|
23
|
+
- seed
|
24
|
+
- close
|
25
|
+
- name:
|
26
|
+
- have_attributes
|
27
|
+
- has_attributes?
|
28
|
+
- receive_messages
|
29
|
+
calls:
|
30
|
+
- keys: '*'
|
31
|
+
- name:
|
32
|
+
- receive
|
33
|
+
- respond_to
|
34
|
+
calls:
|
35
|
+
argument: 1
|
36
|
+
- name: define_negated_matcher
|
37
|
+
defines:
|
38
|
+
argument: 1
|
39
|
+
calls:
|
40
|
+
argument: 2
|
41
|
+
- name:
|
42
|
+
has_prefix: be_
|
43
|
+
calls:
|
44
|
+
itself: true
|
45
|
+
delete_prefix: be_
|
46
|
+
add_suffix: '?'
|
47
|
+
- name:
|
48
|
+
has_prefix: have_
|
49
|
+
calls:
|
50
|
+
itself: true
|
51
|
+
add_suffix: '?'
|
52
|
+
delete_prefix: have_
|
53
|
+
add_prefix: has_
|
54
|
+
- name: let
|
55
|
+
defines:
|
56
|
+
argument: 1
|
data/lib/config/ruby.yml
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
---
|
2
|
+
include_paths:
|
3
|
+
- '*.rb'
|
4
|
+
- Gemfile
|
5
|
+
- '*.erb'
|
6
|
+
- '*.gemspec'
|
7
|
+
- .irbrc
|
8
|
+
- irb.rc
|
9
|
+
- _irbrc
|
10
|
+
- $irbrc
|
11
|
+
|
12
|
+
test_paths:
|
13
|
+
- /tests/
|
14
|
+
- /test/
|
15
|
+
|
16
|
+
rules:
|
17
|
+
- skip: true
|
18
|
+
name:
|
19
|
+
- initialize # called by new
|
20
|
+
- inspect # called by repl
|
21
|
+
- respond_to_missing? # called by respond_to?
|
22
|
+
- method_missing # called by method dispatch
|
23
|
+
- to_s # called by #{}
|
24
|
+
- to_ary
|
25
|
+
- to_str
|
26
|
+
- <=> # called by Comparable
|
27
|
+
- each # called by Enumerable
|
28
|
+
- marshal_dump # called by Marshal.dump
|
29
|
+
- marshal_load # called by Marshal.load
|
30
|
+
- _dump # called by Marshal.dump
|
31
|
+
- _load # called by Marshal.load
|
32
|
+
- name:
|
33
|
+
- send
|
34
|
+
- public_send
|
35
|
+
- __send__
|
36
|
+
- method
|
37
|
+
- respond_to?
|
38
|
+
- instance_method
|
39
|
+
calls:
|
40
|
+
argument: 1
|
41
|
+
- name: attr_reader
|
42
|
+
defines:
|
43
|
+
argument: '*'
|
44
|
+
calls:
|
45
|
+
argument: '*'
|
46
|
+
add_prefix: '@'
|
47
|
+
- name: attr_writer
|
48
|
+
defines:
|
49
|
+
argument: '*'
|
50
|
+
add_suffix: '='
|
51
|
+
- name: attr_accessor
|
52
|
+
defines:
|
53
|
+
- argument: '*'
|
54
|
+
transforms:
|
55
|
+
- original
|
56
|
+
- add_suffix: '='
|
57
|
+
calls:
|
58
|
+
argument: '*'
|
59
|
+
add_prefix: '@'
|
60
|
+
- name: define_method
|
61
|
+
defines:
|
62
|
+
argument: 1
|
63
|
+
- name: alias_method
|
64
|
+
defines:
|
65
|
+
argument: 1
|
66
|
+
calls:
|
67
|
+
argument: 2
|
68
|
+
- name:
|
69
|
+
- instance_variable_get
|
70
|
+
- class_variable_set
|
71
|
+
calls:
|
72
|
+
argument: 1
|
73
|
+
- name:
|
74
|
+
- instance_variable_set
|
75
|
+
- class_variable_set
|
76
|
+
defines:
|
77
|
+
argument: 1
|
@@ -0,0 +1,21 @@
|
|
1
|
+
rules:
|
2
|
+
skip: true
|
3
|
+
name:
|
4
|
+
- before_navigate_to
|
5
|
+
- before_navigate_back
|
6
|
+
- after_navigate_back
|
7
|
+
- before_navigate_forward
|
8
|
+
- after_navigate_forward
|
9
|
+
- before_find
|
10
|
+
- after_find
|
11
|
+
- before_click
|
12
|
+
- before_change_value_of
|
13
|
+
- after_change_value_of
|
14
|
+
- before_execute_script
|
15
|
+
- after_execute_script
|
16
|
+
- before_quit
|
17
|
+
- before_close
|
18
|
+
- after_navigate_to
|
19
|
+
- after_click
|
20
|
+
- after_quit
|
21
|
+
- after_close
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'definition'
|
4
|
+
require_relative 'definition_set'
|
5
|
+
require_relative 'name_rule'
|
6
|
+
require_relative 'transform_rule'
|
7
|
+
require_relative 'hash_rule'
|
8
|
+
|
9
|
+
module Leftovers
|
10
|
+
class ArgumentRule # rubocop:disable Metrics/ClassLength
|
11
|
+
def self.wrap(rules, definer: false) # rubocop:disable Metrics/MethodLength
|
12
|
+
case rules
|
13
|
+
when Array
|
14
|
+
rules.flat_map { |r| wrap(r, definer: definer) }
|
15
|
+
when Hash
|
16
|
+
[new(**rules, definer: definer)]
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ADDITIONAL_VALID_KEYS = Leftovers::TransformRule::VALID_TRANSFORMS + %i{if unless}
|
23
|
+
def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
24
|
+
argument: nil,
|
25
|
+
arguments: nil,
|
26
|
+
key: nil,
|
27
|
+
keys: nil,
|
28
|
+
itself: false,
|
29
|
+
linked_transforms: nil,
|
30
|
+
transforms: nil,
|
31
|
+
definer: false,
|
32
|
+
**options
|
33
|
+
)
|
34
|
+
assert_valid_keys(options, ADDITIONAL_VALID_KEYS)
|
35
|
+
prepare_argument(argument, arguments)
|
36
|
+
@key = prepare_key(key, keys)
|
37
|
+
@itself = itself
|
38
|
+
|
39
|
+
unless @positions || @keywords || @all_positions || @all_keywords || @key || @itself
|
40
|
+
raise ArgumentError, "require at least one of 'argument(s)', 'key(s)', itself"
|
41
|
+
end
|
42
|
+
|
43
|
+
@if = prepare_condition(options.delete(:if))
|
44
|
+
@unless = prepare_condition(options.delete(:unless))
|
45
|
+
@transforms = prepare_transform(options, transforms, linked_transforms)
|
46
|
+
@definer = definer
|
47
|
+
end
|
48
|
+
|
49
|
+
def prepare_transform(options, transforms, linked_transforms) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
50
|
+
if linked_transforms && transforms
|
51
|
+
raise ArgumentError, 'Only use one of linked_transforms/transforms'
|
52
|
+
end
|
53
|
+
return if !linked_transforms && !transforms && options.empty?
|
54
|
+
|
55
|
+
if !(linked_transforms || transforms)
|
56
|
+
@transform = TransformRule.new(options)
|
57
|
+
else
|
58
|
+
@linked = !!linked_transforms
|
59
|
+
|
60
|
+
transforms = (linked_transforms || transforms).map do |transform|
|
61
|
+
transform = { transform.to_sym => true } if transform.is_a?(String)
|
62
|
+
Leftovers::TransformRule.new(options.merge(transform))
|
63
|
+
end
|
64
|
+
|
65
|
+
if transforms.length <= 1
|
66
|
+
@transform = transforms.first
|
67
|
+
else
|
68
|
+
@transforms = transforms
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def prepare_condition(conditions)
|
74
|
+
Array.wrap(conditions).each do |cond|
|
75
|
+
cond[:has_argument] = HashRule.new(cond[:has_argument]) if cond[:has_argument]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def prepare_key(key, keys)
|
80
|
+
raise ArgumentError, 'Only use one of key/keys' if key && keys
|
81
|
+
|
82
|
+
key || keys
|
83
|
+
end
|
84
|
+
|
85
|
+
def prepare_argument(argument, arguments) # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
86
|
+
raise ArgumentError, 'Only use one of argument/arguments' if argument && arguments
|
87
|
+
|
88
|
+
positions = Set.new
|
89
|
+
keywords = []
|
90
|
+
|
91
|
+
Array.each_or_self(argument || arguments) do |arg|
|
92
|
+
case arg
|
93
|
+
when '*'
|
94
|
+
@all_positions = true
|
95
|
+
when '**'
|
96
|
+
@all_keywords = true
|
97
|
+
when Integer
|
98
|
+
positions << arg - 1
|
99
|
+
when String, Symbol, Hash
|
100
|
+
keywords << arg
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@positions = positions unless @all_positions || positions.empty? || @all_positions
|
105
|
+
@keywords = NameRule.new(keywords) unless @all_keywords || keywords.empty?
|
106
|
+
end
|
107
|
+
|
108
|
+
def matches(method_node) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
109
|
+
return Array::EMPTY unless all_conditions_match?(method_node)
|
110
|
+
|
111
|
+
result = []
|
112
|
+
|
113
|
+
if @all_positions
|
114
|
+
result.gather values(method_node.positional_arguments, method_node)
|
115
|
+
elsif @positions
|
116
|
+
result.gather values(method_node.positional_arguments_at(@positions).compact, method_node)
|
117
|
+
end
|
118
|
+
|
119
|
+
if @keywords || @all_keywords || @key
|
120
|
+
result.gather hash_values(method_node.kwargs, method_node)
|
121
|
+
end
|
122
|
+
result << method_value(method_node) if @itself
|
123
|
+
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
def values(value_nodes, method_node)
|
128
|
+
value_nodes.flat_map { |value_node| value(value_node, method_node) }.compact
|
129
|
+
end
|
130
|
+
|
131
|
+
def all_conditions_match?(method_node)
|
132
|
+
@if.all? { |c| condition_match?(c, method_node) } &&
|
133
|
+
@unless.all? { |c| !condition_match?(c, method_node) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def condition_match?(condition, method_name)
|
137
|
+
return unless condition[:has_argument]
|
138
|
+
|
139
|
+
hash_node = method_name.kwargs
|
140
|
+
return false unless hash_node
|
141
|
+
|
142
|
+
hash_node.each_pair.any? { |key, value| condition[:has_argument].match_pair?(key, value) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def hash_values(hash_node, method_node) # rubocop:disable Metrics/MethodLength
|
146
|
+
return unless hash_node
|
147
|
+
|
148
|
+
value_nodes = []
|
149
|
+
value_nodes.concat hash_node.keys if @key
|
150
|
+
|
151
|
+
if @all_keywords
|
152
|
+
value_nodes.concat(hash_node.values)
|
153
|
+
elsif @keywords
|
154
|
+
value_nodes.concat(hash_node.values_at_match(@keywords))
|
155
|
+
end
|
156
|
+
|
157
|
+
values(value_nodes, method_node)
|
158
|
+
end
|
159
|
+
|
160
|
+
def value(value_node, method_node) # rubocop:disable Metrics/MethodLength
|
161
|
+
return unless value_node
|
162
|
+
|
163
|
+
value_node = value_node.unwrap_freeze
|
164
|
+
|
165
|
+
case value_node.type
|
166
|
+
when :array
|
167
|
+
values(value_node.values, method_node)
|
168
|
+
when :hash
|
169
|
+
hash_values(value_node, method_node)
|
170
|
+
when :str, :sym
|
171
|
+
symbol_values(value_node, method_node)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
SPLIT = /[.:]+/.freeze
|
176
|
+
def symbol_values(symbol_node, method_node) # rubocop:disable Metrics/MethodLength
|
177
|
+
subnodes = symbol_node.to_s.split(SPLIT).flat_map { |s| transform(s, method_node) }
|
178
|
+
|
179
|
+
return subnodes unless @definer
|
180
|
+
|
181
|
+
location = symbol_node.loc.expression
|
182
|
+
if @linked
|
183
|
+
Leftovers::DefinitionSet.new(subnodes, location: location, method_node: method_node)
|
184
|
+
else
|
185
|
+
subnodes.map do |subnode|
|
186
|
+
Leftovers::Definition.new(subnode, location: location, method_node: method_node)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def method_value(method_node)
|
192
|
+
value = transform(method_node.to_s, method_node)
|
193
|
+
|
194
|
+
return value unless @definer
|
195
|
+
|
196
|
+
Leftovers::Definition.new(value, method_node: method_node)
|
197
|
+
end
|
198
|
+
|
199
|
+
def transform(string, method_node)
|
200
|
+
return string.to_sym unless @transform || @transforms
|
201
|
+
return @transform.transform(string, method_node) if @transform
|
202
|
+
|
203
|
+
@transforms.map do |transform|
|
204
|
+
transform.transform(string, method_node)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def assert_valid_keys(options, keys)
|
209
|
+
invalid = options.keys - keys
|
210
|
+
|
211
|
+
return if invalid.empty?
|
212
|
+
|
213
|
+
raise ArgumentError, "unknown keyword#{'s' if invalid.length > 1}: #{invalid.join(', ')}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
ruby_version = Gem::Version.new(RUBY_VERSION)
|
5
|
+
unless ruby_version >= Gem::Version.new('2.5')
|
6
|
+
module StringDeletePrefixSuffix
|
7
|
+
refine ::String do
|
8
|
+
def delete_prefix!(str)
|
9
|
+
slice!(0..(str.length - 1)) if start_with?(str)
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def delete_suffix!(str)
|
14
|
+
slice!(-str.length..-1) if end_with?(str)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_prefix(str)
|
19
|
+
dup.delete_prefix!(str)
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_suffix(str)
|
23
|
+
dup.delete_suffix!(str)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
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
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require_relative '../leftovers'
|
5
|
+
require_relative 'version'
|
6
|
+
|
7
|
+
module Leftovers
|
8
|
+
class CLI
|
9
|
+
attr_reader :argv, :stdout, :stderr
|
10
|
+
|
11
|
+
def initialize(argv: [], stdout: StringIO.new, stderr: StringIO.new)
|
12
|
+
@argv = argv
|
13
|
+
@stdout = stdout
|
14
|
+
@stderr = stderr
|
15
|
+
|
16
|
+
parse_options
|
17
|
+
|
18
|
+
exit Leftovers.run(stdout: stdout, stderr: stderr)
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
22
|
+
opts = OptionParser.new
|
23
|
+
Leftovers.parallel = true
|
24
|
+
Leftovers.progress = true
|
25
|
+
|
26
|
+
opts.banner = 'Usage: leftovers [options]'
|
27
|
+
opts.on('-q', '--quiet', 'Silences output') { Leftovers.quiet = true }
|
28
|
+
opts.on('--[no-]parallel', 'Run in parallel or not, default --parallel') do |p|
|
29
|
+
Leftovers.parallel = p
|
30
|
+
end
|
31
|
+
opts.on('--[no-]progress', 'Show progress counts or not, default --progress') do |p|
|
32
|
+
Leftovers.progress = p
|
33
|
+
end
|
34
|
+
opts.on('-v', '--version', 'Returns the current version') do
|
35
|
+
stdout.puts(Leftovers::Version)
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
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
|
41
|
+
end
|
42
|
+
opts.on('-h', '--help', 'Shows this message') do
|
43
|
+
stdout.puts(opts.help)
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.parse(argv)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fast_ignore'
|
4
|
+
require 'set'
|
5
|
+
require 'parallel'
|
6
|
+
require_relative 'file_collector'
|
7
|
+
require_relative 'file_list'
|
8
|
+
|
9
|
+
module Leftovers
|
10
|
+
class Collector
|
11
|
+
attr_reader :calls
|
12
|
+
attr_reader :test_calls
|
13
|
+
attr_reader :definitions
|
14
|
+
|
15
|
+
def initialize # rubocop:disable Metrics/MethodLength
|
16
|
+
@calls = []
|
17
|
+
@test_calls = []
|
18
|
+
@definitions = []
|
19
|
+
@count = 0
|
20
|
+
@count_calls = 0
|
21
|
+
@count_definitions = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def collect
|
25
|
+
collect_file_list(Leftovers::FileList.new)
|
26
|
+
print_progress
|
27
|
+
Leftovers.newline
|
28
|
+
@calls = @calls.to_set.freeze
|
29
|
+
@test_calls = @test_calls.to_set.freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
def collect_file_list(list)
|
33
|
+
if Leftovers.parallel?
|
34
|
+
Parallel.each(list, finish: method(:finish_parallel), &method(:collect_file))
|
35
|
+
else
|
36
|
+
list.each { |file| finish_parallel(nil, nil, collect_file(file)) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def collect_file(file)
|
41
|
+
file_collector = Leftovers::FileCollector.new(file.ruby, file)
|
42
|
+
file_collector.collect
|
43
|
+
|
44
|
+
file_collector.to_h
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_progress
|
48
|
+
Leftovers.print(
|
49
|
+
"checked #{@count} files, collected #{@count_calls} calls, #{@count_definitions} definitions\r" # rubocop:disable Layout/LineLength
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def finish_parallel(_, _, result) # rubocop:disable Metrics/MethodLength
|
54
|
+
@count += 1
|
55
|
+
@count_calls += result[:calls].length
|
56
|
+
@count_definitions += result[:definitions].length
|
57
|
+
print_progress if Leftovers.progress?
|
58
|
+
if result[:test?]
|
59
|
+
@test_calls.concat(result[:calls])
|
60
|
+
else
|
61
|
+
@calls.concat(result[:calls])
|
62
|
+
end
|
63
|
+
|
64
|
+
@definitions.concat(result[:definitions])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require_relative 'rule'
|
5
|
+
|
6
|
+
module Leftovers
|
7
|
+
class Config
|
8
|
+
# :nocov:
|
9
|
+
using ::Leftovers::YAMLSymbolizeNames if defined?(::Leftovers::YAMLSymbolizeNames)
|
10
|
+
# :nocov:
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
def initialize(
|
15
|
+
name,
|
16
|
+
path: ::File.join(__dir__, '..', 'config', "#{name}.yml"),
|
17
|
+
content: (::File.exist?(path) ? ::File.read(path) : '')
|
18
|
+
)
|
19
|
+
@name = name.to_sym
|
20
|
+
@path = path
|
21
|
+
@content = content
|
22
|
+
end
|
23
|
+
|
24
|
+
def gems
|
25
|
+
@gems ||= Array(yaml[:gems]).map(&:to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def exclude_paths
|
29
|
+
@exclude_paths ||= Array(yaml[:exclude_paths])
|
30
|
+
end
|
31
|
+
|
32
|
+
def include_paths
|
33
|
+
@include_paths ||= Array(yaml[:include_paths])
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_paths
|
37
|
+
@test_paths ||= Array(yaml[:test_paths])
|
38
|
+
end
|
39
|
+
|
40
|
+
def rules
|
41
|
+
@rules ||= Rule.wrap(yaml[:rules])
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
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
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
class Array
|
6
|
+
# concat, # push, # add, #
|
7
|
+
EMPTY = [].freeze
|
8
|
+
|
9
|
+
def gather(other)
|
10
|
+
case other
|
11
|
+
when Array, Set then concat(other)
|
12
|
+
when nil then self
|
13
|
+
else self.<< other
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.wrap(value)
|
18
|
+
case value
|
19
|
+
when nil then EMPTY
|
20
|
+
when Array then value
|
21
|
+
else [value]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.each_or_self(value, &block)
|
26
|
+
case value
|
27
|
+
when nil then nil
|
28
|
+
when Array then value.each(&block)
|
29
|
+
else block.call(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|