leftovers 0.5.5 → 0.6.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -1
  4. data/docs/Configuration.md +106 -2
  5. data/leftovers.gemspec +7 -4
  6. data/lib/config/ruby.yml +7 -0
  7. data/lib/leftovers/collector.rb +3 -1
  8. data/lib/leftovers/config.rb +8 -4
  9. data/lib/leftovers/config_validator/schema_hash.rb +170 -149
  10. data/lib/leftovers/config_validator.rb +1 -0
  11. data/lib/leftovers/definition.rb +1 -1
  12. data/lib/leftovers/dynamic_processors/call.rb +0 -4
  13. data/lib/leftovers/dynamic_processors/call_definition.rb +0 -4
  14. data/lib/leftovers/dynamic_processors/definition.rb +0 -4
  15. data/lib/leftovers/file.rb +52 -10
  16. data/lib/leftovers/file_collector.rb +33 -2
  17. data/lib/leftovers/json.rb +28 -0
  18. data/lib/leftovers/matcher_builders/document.rb +13 -0
  19. data/lib/leftovers/matcher_builders/node.rb +3 -1
  20. data/lib/leftovers/matcher_builders/node_has_argument.rb +3 -4
  21. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +1 -1
  22. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +1 -1
  23. data/lib/leftovers/matcher_builders.rb +1 -0
  24. data/lib/leftovers/matchers/all.rb +0 -4
  25. data/lib/leftovers/matchers/and.rb +0 -4
  26. data/lib/leftovers/matchers/any.rb +0 -4
  27. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +1 -7
  28. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +1 -7
  29. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +0 -4
  30. data/lib/leftovers/matchers/node_name.rb +0 -4
  31. data/lib/leftovers/matchers/node_pair_value.rb +0 -4
  32. data/lib/leftovers/matchers/node_path.rb +0 -4
  33. data/lib/leftovers/matchers/node_scalar_value.rb +0 -4
  34. data/lib/leftovers/matchers/node_type.rb +0 -4
  35. data/lib/leftovers/matchers/not.rb +0 -4
  36. data/lib/leftovers/matchers/or.rb +0 -4
  37. data/lib/leftovers/merged_config.rb +19 -1
  38. data/lib/leftovers/processor_builders/dynamic.rb +2 -1
  39. data/lib/leftovers/todo_reporter.rb +10 -35
  40. data/lib/leftovers/value_processors/delete_prefix.rb +0 -6
  41. data/lib/leftovers/value_processors/delete_suffix.rb +0 -6
  42. data/lib/leftovers/value_processors/keyword.rb +0 -4
  43. data/lib/leftovers/value_processors/keyword_argument.rb +0 -4
  44. data/lib/leftovers/value_processors/return_definition.rb +0 -4
  45. data/lib/leftovers/version.rb +1 -1
  46. data/lib/leftovers/yaml.rb +73 -0
  47. data/lib/leftovers.rb +6 -7
  48. metadata +33 -16
  49. data/lib/leftovers/backports.rb +0 -40
@@ -10,7 +10,7 @@ module Leftovers
10
10
  ::Leftovers::MatcherBuilders::NodeName.build(pat)
11
11
  when ::Hash
12
12
  build_from_hash(**pat)
13
- # :nocov:
13
+ # :nocov:
14
14
  else raise
15
15
  # :nocov:
16
16
  end
@@ -19,6 +19,7 @@ module Leftovers
19
19
 
20
20
  def self.build_from_hash( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
21
21
  names: nil, match: nil, has_prefix: nil, has_suffix: nil,
22
+ document: false,
22
23
  paths: nil,
23
24
  has_arguments: nil,
24
25
  has_receiver: nil,
@@ -29,6 +30,7 @@ module Leftovers
29
30
  names,
30
31
  { match: match, has_prefix: has_prefix, has_suffix: has_suffix }.compact
31
32
  ]),
33
+ ::Leftovers::MatcherBuilders::Document.build(document),
32
34
  ::Leftovers::MatcherBuilders::NodePath.build(paths),
33
35
  ::Leftovers::MatcherBuilders::NodeHasArgument.build(has_arguments),
34
36
  ::Leftovers::MatcherBuilders::NodeHasReceiver.build(has_receiver),
@@ -26,12 +26,10 @@ module Leftovers
26
26
 
27
27
  ::Leftovers.each_or_self(at) do |k|
28
28
  case k
29
- when '*'
29
+ when '*', ::Integer
30
30
  positions << k
31
31
  when ::String, ::Hash
32
32
  keys << k
33
- when ::Integer
34
- positions << k
35
33
  # :nocov:
36
34
  else raise
37
35
  # :nocov:
@@ -62,7 +60,8 @@ module Leftovers
62
60
  elsif positions
63
61
  ::Leftovers::MatcherBuilders::NodeHasPositionalArgument.build(positions, value_matcher)
64
62
  # :nocov:
65
- else raise
63
+ else
64
+ raise
66
65
  # :nocov:
67
66
  end
68
67
 
@@ -6,7 +6,7 @@ module Leftovers
6
6
  class << self
7
7
  def build(keywords, value_matcher) # rubocop:disable Metrics/MethodLength
8
8
  value_matcher = ::Leftovers::MatcherBuilders::NodePairValue.build(value_matcher)
9
- keyword_matcher = if ::Leftovers.each_or_self(keywords).any? { |x| x == '**' }
9
+ keyword_matcher = if ::Leftovers.each_or_self(keywords).include?('**')
10
10
  ::Leftovers::Matchers::NodeType.new(:pair)
11
11
  else
12
12
  ::Leftovers::MatcherBuilders::NodePairName.build(keywords)
@@ -14,7 +14,7 @@ module Leftovers
14
14
  end
15
15
  end
16
16
  elsif positions
17
- pos = 0 if ::Leftovers.each_or_self(positions).any? { |x| x == '*' }
17
+ pos = 0 if ::Leftovers.each_or_self(positions).include?('*')
18
18
  pos ||= ::Leftovers.each_or_self(positions).min
19
19
 
20
20
  ::Leftovers::Matchers::NodeHasPositionalArgument.new(pos)
@@ -4,6 +4,7 @@ module Leftovers
4
4
  module MatcherBuilders
5
5
  autoload(:AndNot, "#{__dir__}/matcher_builders/and_not")
6
6
  autoload(:And, "#{__dir__}/matcher_builders/and")
7
+ autoload(:Document, "#{__dir__}/matcher_builders/document")
7
8
  autoload(:Name, "#{__dir__}/matcher_builders/name")
8
9
  autoload(:Node, "#{__dir__}/matcher_builders/node")
9
10
  autoload(:NodeHasArgument, "#{__dir__}/matcher_builders/node_has_argument")
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class All
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matchers)
11
7
  @matchers = matchers
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class And
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(lhs, rhs)
11
7
  @lhs = lhs
12
8
  @rhs = rhs
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Any
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  attr_reader :matchers
11
7
 
12
8
  def initialize(matchers)
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasAnyKeywordArgument
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(pair_matcher)
11
7
  @pair_matcher = pair_matcher
12
8
 
@@ -17,9 +13,7 @@ module Leftovers
17
13
  kwargs = node.kwargs
18
14
  return false unless kwargs
19
15
 
20
- kwargs.children.any? do |pair|
21
- @pair_matcher === pair
22
- end
16
+ kwargs.children.any?(@pair_matcher)
23
17
  end
24
18
 
25
19
  freeze
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasAnyPositionalArgumentWithValue
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher)
11
7
  @matcher = matcher
12
8
 
@@ -17,9 +13,7 @@ module Leftovers
17
13
  args = node.positional_arguments
18
14
  return false unless args
19
15
 
20
- args.any? do |value|
21
- @matcher === value
22
- end
16
+ args.any?(@matcher)
23
17
  end
24
18
 
25
19
  freeze
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasPositionalArgumentWithValue
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(position, matcher)
11
7
  @position = position
12
8
  @matcher = matcher
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeName
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(name_matcher)
11
7
  @name_matcher = name_matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodePairValue
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(value_matcher)
11
7
  @value_matcher = value_matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodePath
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(path_matcher)
11
7
  @path_matcher = path_matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeScalarValue
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher)
11
7
  @matcher = matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeType
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(type_matcher)
11
7
  @type_matcher = type_matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Not
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher)
11
7
  @matcher = matcher
12
8
 
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Or
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  attr_reader :lhs, :rhs
11
7
 
12
8
  def initialize(lhs, rhs)
@@ -4,7 +4,7 @@ require 'set'
4
4
  require 'fast_ignore'
5
5
 
6
6
  module Leftovers
7
- class MergedConfig
7
+ class MergedConfig # rubocop:disable Metrics/ClassLength
8
8
  def initialize(load_defaults: false)
9
9
  @configs = []
10
10
  @loaded_configs = Set.new
@@ -43,6 +43,8 @@ module Leftovers
43
43
  @test_paths
44
44
  @haml_paths
45
45
  @slim_paths
46
+ @yaml_paths
47
+ @json_paths
46
48
  @erb_paths
47
49
  @dynamic
48
50
  @keep
@@ -87,6 +89,22 @@ module Leftovers
87
89
  )
88
90
  end
89
91
 
92
+ def yaml_paths
93
+ @yaml_paths ||= FastIgnore.new(
94
+ include_rules: @configs.flat_map(&:yaml_paths),
95
+ gitignore: false,
96
+ root: Leftovers.pwd
97
+ )
98
+ end
99
+
100
+ def json_paths
101
+ @json_paths ||= FastIgnore.new(
102
+ include_rules: @configs.flat_map(&:json_paths),
103
+ gitignore: false,
104
+ root: Leftovers.pwd
105
+ )
106
+ end
107
+
90
108
  def erb_paths
91
109
  @erb_paths ||= FastIgnore.new(
92
110
  include_rules: @configs.flat_map(&:erb_paths),
@@ -17,7 +17,8 @@ module Leftovers
17
17
  elsif call
18
18
  ::Leftovers::DynamicProcessors::Call.new(matcher, call)
19
19
  # :nocov:
20
- else raise
20
+ else
21
+ raise
21
22
  # :nocov:
22
23
  end
23
24
  end
@@ -64,54 +64,29 @@ module Leftovers
64
64
  end
65
65
 
66
66
  def todo_data(only_test, none)
67
- none_test = none.select(&:test?)
68
- none_non_test = none.reject(&:test?)
69
67
  [
70
- test_only_data(none_test),
71
- keep_data(only_test, none_non_test)
68
+ list_data(
69
+ :test_only, 'Only directly called in tests', only_test
70
+ ),
71
+ list_data(
72
+ :keep, 'Not directly called at all', none
73
+ )
72
74
  ].compact.join
73
75
  end
74
76
 
75
- def test_only_data(list)
77
+ def list_data(key, title, list)
76
78
  return if list.empty?
77
79
 
78
80
  <<~YML
79
- test_only:
80
- #{generate_list('Defined in tests:', list).chomp}
81
- YML
82
- end
83
-
84
- def keep_data(only_test, none_non_test)
85
- return if only_test.empty? && none_non_test.empty?
86
-
87
- <<~YML.chomp
88
- keep:
89
- #{keep_test_called_data(only_test)}#{keep_never_called_data(none_non_test)}
90
- YML
91
- end
92
-
93
- def keep_test_called_data(list)
94
- return if list.empty?
95
-
96
- generate_list('Only directly called in tests:', list)
97
- end
98
-
99
- def keep_never_called_data(list)
100
- return if list.empty?
101
-
102
- generate_list('Not directly called at all:', list)
103
- end
104
-
105
- def generate_list(title, list)
106
- <<~YML
107
- # #{title}
81
+ #{key}:
82
+ # #{title}:
108
83
  #{print_definition_list(list)}
109
84
 
110
85
  YML
111
86
  end
112
87
 
113
88
  def print_definition_list(definition_list)
114
- definition_list.map { |definition| print_definition(definition) }.join("\n")
89
+ definition_list.map { |definition| print_definition(definition) }.sort.join("\n")
115
90
  end
116
91
 
117
92
  def print_definition(definition)
@@ -3,12 +3,6 @@
3
3
  module Leftovers
4
4
  module ValueProcessors
5
5
  class DeletePrefix
6
- # :nocov:
7
- if defined?(::Leftovers::Backports::StringDeletePrefixSuffix)
8
- using ::Leftovers::Backports::StringDeletePrefixSuffix
9
- end
10
- # :nocov:
11
-
12
6
  def initialize(prefix, then_processor)
13
7
  @prefix = prefix
14
8
  @then_processor = then_processor
@@ -3,12 +3,6 @@
3
3
  module Leftovers
4
4
  module ValueProcessors
5
5
  class DeleteSuffix
6
- # :nocov:
7
- if defined?(::Leftovers::Backports::StringDeletePrefixSuffix)
8
- using ::Leftovers::Backports::StringDeletePrefixSuffix
9
- end
10
- # :nocov:
11
-
12
6
  def initialize(suffix, then_processor)
13
7
  @suffix = suffix
14
8
  @then_processor = then_processor
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module ValueProcessors
5
5
  class Keyword
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher, then_processor)
11
7
  @matcher = matcher
12
8
  @then_processor = then_processor
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module ValueProcessors
5
5
  class KeywordArgument
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher, then_processor)
11
7
  @matcher = matcher
12
8
  @then_processor = then_processor
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module ValueProcessors
5
5
  module ReturnDefinition
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def self.process(str, node, method_node)
11
7
  return unless str
12
8
  return if str.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leftovers
4
- VERSION = '0.5.5'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Leftovers
6
+ module YAML
7
+ class Builder < ::Psych::TreeBuilder
8
+ def initialize
9
+ @constants = []
10
+
11
+ super
12
+ end
13
+
14
+ def add_constant_for_tag(tag, value = nil)
15
+ match = %r{\A!ruby/[^:]*(?::(.*))?\z}.match(tag)
16
+ return unless match
17
+
18
+ @constants << (match[1] || value)
19
+ end
20
+
21
+ def start_mapping(_anchor, tag, *rest) # leftovers:keep
22
+ add_constant_for_tag(tag)
23
+ tag = nil
24
+
25
+ super
26
+ end
27
+
28
+ def start_sequence(_anchor, tag, *rest) # leftovers:keep
29
+ add_constant_for_tag(tag)
30
+ tag = nil
31
+
32
+ super
33
+ end
34
+
35
+ def scalar(value, _anchor, tag, *rest) # leftovers:keep
36
+ add_constant_for_tag(tag, value)
37
+ tag = nil
38
+
39
+ super
40
+ end
41
+
42
+ def to_ruby_file
43
+ <<~RUBY
44
+ __leftovers_document(#{to_ruby_argument(root.to_ruby.first)})
45
+ #{@constants.join("\n")}
46
+ RUBY
47
+ end
48
+
49
+ private
50
+
51
+ def to_ruby_argument(value)
52
+ ruby = value.inspect
53
+ return ruby unless value.is_a?(Array)
54
+
55
+ ruby.delete_prefix!('[')
56
+ ruby.delete_suffix!(']')
57
+
58
+ ruby
59
+ end
60
+ end
61
+
62
+ def self.precompile(yaml, name)
63
+ builder = ::Leftovers::YAML::Builder.new
64
+ parser = ::Psych::Parser.new(builder)
65
+ parser.parse(yaml, name.relative_path)
66
+
67
+ builder.to_ruby_file
68
+ rescue ::Psych::SyntaxError => e
69
+ Leftovers.warn "#{e.class}: #{e.message}"
70
+ ''
71
+ end
72
+ end
73
+ end
data/lib/leftovers.rb CHANGED
@@ -4,7 +4,6 @@ module Leftovers # rubocop:disable Metrics/ModuleLength
4
4
  class Error < ::StandardError; end
5
5
 
6
6
  autoload(:AST, "#{__dir__}/leftovers/ast")
7
- autoload(:Backports, "#{__dir__}/leftovers/backports")
8
7
  autoload(:CLI, "#{__dir__}/leftovers/cli")
9
8
  autoload(:Collector, "#{__dir__}/leftovers/collector")
10
9
  autoload(:ConfigValidator, "#{__dir__}/leftovers/config_validator")
@@ -17,6 +16,8 @@ module Leftovers # rubocop:disable Metrics/ModuleLength
17
16
  autoload(:FileList, "#{__dir__}/leftovers/file_list")
18
17
  autoload(:File, "#{__dir__}/leftovers/file")
19
18
  autoload(:Haml, "#{__dir__}/leftovers/haml")
19
+ autoload(:YAML, "#{__dir__}/leftovers/yaml")
20
+ autoload(:JSON, "#{__dir__}/leftovers/json")
20
21
  autoload(:MatcherBuilders, "#{__dir__}/leftovers/matcher_builders")
21
22
  autoload(:Matchers, "#{__dir__}/leftovers/matchers")
22
23
  autoload(:MergedConfig, "#{__dir__}/leftovers/merged_config")
@@ -147,12 +148,10 @@ module Leftovers # rubocop:disable Metrics/ModuleLength
147
148
  @try_require_cache ||= {}
148
149
 
149
150
  @try_require_cache.fetch(requirable) do
150
- begin
151
- require requirable
152
- @try_require_cache[requirable] = true
153
- rescue LoadError
154
- @try_require_cache[requirable] = false
155
- end
151
+ require requirable
152
+ @try_require_cache[requirable] = true
153
+ rescue LoadError
154
+ @try_require_cache[requirable] = false
156
155
  end
157
156
  end
158
157
  end