leftovers 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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