leftovers 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -3
  3. data/README.md +8 -7
  4. data/docs/Configuration.md +241 -105
  5. data/docs/Custom-Precompilers.md +44 -0
  6. data/leftovers.gemspec +3 -2
  7. data/lib/config/actioncable.yml +36 -4
  8. data/lib/config/actionmailbox.yml +28 -0
  9. data/lib/config/actionmailer.yml +92 -16
  10. data/lib/config/actionpack.yml +130 -32
  11. data/lib/config/actiontext.yml +56 -0
  12. data/lib/config/actionview.yml +194 -44
  13. data/lib/config/activejob.yml +15 -8
  14. data/lib/config/activemodel.yml +175 -18
  15. data/lib/config/activerecord.yml +397 -86
  16. data/lib/config/activestorage.yml +26 -0
  17. data/lib/config/activesupport.yml +168 -25
  18. data/lib/config/haml.yml +4 -2
  19. data/lib/config/leftovers.yml +48 -0
  20. data/lib/config/rails.yml +8 -4
  21. data/lib/config/railties.yml +18 -0
  22. data/lib/config/ruby.yml +473 -57
  23. data/lib/config/slim.yml +4 -2
  24. data/lib/config/test-unit.yml +8 -0
  25. data/lib/leftovers/ast/array_node.rb +12 -0
  26. data/lib/leftovers/ast/block_node.rb +12 -0
  27. data/lib/leftovers/ast/builder.rb +24 -5
  28. data/lib/leftovers/ast/casgn_node.rb +20 -0
  29. data/lib/leftovers/ast/const_node.rb +15 -0
  30. data/lib/leftovers/ast/def_node.rb +15 -0
  31. data/lib/leftovers/ast/defs_node.rb +15 -0
  32. data/lib/leftovers/ast/false_node.rb +24 -0
  33. data/lib/leftovers/ast/has_arguments.rb +31 -0
  34. data/lib/leftovers/ast/hash_node.rb +17 -0
  35. data/lib/leftovers/ast/module_node.rb +16 -0
  36. data/lib/leftovers/ast/nil_node.rb +23 -0
  37. data/lib/leftovers/ast/node.rb +39 -91
  38. data/lib/leftovers/ast/numeric_node.rb +22 -0
  39. data/lib/leftovers/ast/send_node.rb +25 -0
  40. data/lib/leftovers/ast/str_node.rb +24 -0
  41. data/lib/leftovers/ast/sym_node.rb +25 -0
  42. data/lib/leftovers/ast/true_node.rb +24 -0
  43. data/lib/leftovers/ast/var_node.rb +14 -0
  44. data/lib/leftovers/ast/vasgn_node.rb +20 -0
  45. data/lib/leftovers/ast.rb +18 -0
  46. data/lib/leftovers/cli.rb +12 -1
  47. data/lib/leftovers/collector.rb +2 -1
  48. data/lib/leftovers/comparable_instance.rb +18 -0
  49. data/lib/leftovers/config.rb +3 -42
  50. data/lib/leftovers/config_loader/argument_position_schema.rb +13 -0
  51. data/lib/leftovers/config_loader/argumentless_transform_schema.rb +21 -0
  52. data/lib/leftovers/config_loader/array_schema.rb +53 -0
  53. data/lib/leftovers/config_loader/attribute.rb +56 -0
  54. data/lib/leftovers/config_loader/built_in_precompiler_schema.rb +13 -0
  55. data/lib/leftovers/config_loader/document_schema.rb +49 -0
  56. data/lib/leftovers/config_loader/dynamic_schema.rb +18 -0
  57. data/lib/leftovers/config_loader/has_argument_schema.rb +13 -0
  58. data/lib/leftovers/config_loader/has_value_schema.rb +22 -0
  59. data/lib/leftovers/config_loader/inherit_schema_attributes.rb +22 -0
  60. data/lib/leftovers/config_loader/keep_test_only_schema.rb +13 -0
  61. data/lib/leftovers/config_loader/keyword_argument_schema.rb +13 -0
  62. data/lib/leftovers/config_loader/node.rb +106 -0
  63. data/lib/leftovers/config_loader/object_schema.rb +117 -0
  64. data/lib/leftovers/config_loader/precompile_schema.rb +12 -0
  65. data/lib/leftovers/config_loader/precompiler_schema.rb +11 -0
  66. data/lib/leftovers/config_loader/privacy_processor_schema.rb +10 -0
  67. data/lib/leftovers/config_loader/privacy_schema.rb +15 -0
  68. data/lib/leftovers/config_loader/regexp_schema.rb +27 -0
  69. data/lib/leftovers/config_loader/require_schema.rb +11 -0
  70. data/lib/leftovers/config_loader/rule_pattern_schema.rb +20 -0
  71. data/lib/leftovers/config_loader/scalar_argument_schema.rb +14 -0
  72. data/lib/leftovers/config_loader/scalar_value_schema.rb +22 -0
  73. data/lib/leftovers/config_loader/schema.rb +33 -0
  74. data/lib/leftovers/config_loader/string_enum_schema.rb +62 -0
  75. data/lib/leftovers/config_loader/string_pattern_schema.rb +14 -0
  76. data/lib/leftovers/config_loader/string_schema.rb +14 -0
  77. data/lib/leftovers/config_loader/string_value_processor_schema.rb +11 -0
  78. data/lib/leftovers/config_loader/suggester.rb +22 -0
  79. data/lib/leftovers/config_loader/transform_schema.rb +32 -0
  80. data/lib/leftovers/config_loader/true_schema.rb +18 -0
  81. data/lib/leftovers/config_loader/value_matcher_condition_schema.rb +13 -0
  82. data/lib/leftovers/config_loader/value_matcher_schema.rb +21 -0
  83. data/lib/leftovers/config_loader/value_or_array_schema.rb +34 -0
  84. data/lib/leftovers/config_loader/value_or_object_schema.rb +40 -0
  85. data/lib/leftovers/config_loader/value_processor_schema.rb +14 -0
  86. data/lib/leftovers/config_loader/value_type_schema.rb +17 -0
  87. data/lib/leftovers/config_loader.rb +93 -0
  88. data/lib/leftovers/definition_collection.rb +37 -0
  89. data/lib/leftovers/definition_node.rb +6 -17
  90. data/lib/leftovers/definition_node_set.rb +19 -0
  91. data/lib/leftovers/definition_to_add.rb +31 -0
  92. data/lib/leftovers/file.rb +13 -55
  93. data/lib/leftovers/file_collector/comments_processor.rb +57 -0
  94. data/lib/leftovers/file_collector/node_processor.rb +131 -0
  95. data/lib/leftovers/file_collector.rb +62 -202
  96. data/lib/leftovers/file_list.rb +3 -2
  97. data/lib/leftovers/matcher_builders/and.rb +26 -9
  98. data/lib/leftovers/matcher_builders/and_not.rb +7 -5
  99. data/lib/leftovers/matcher_builders/name.rb +18 -17
  100. data/lib/leftovers/matcher_builders/node.rb +60 -30
  101. data/lib/leftovers/matcher_builders/node_has_argument.rb +48 -52
  102. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +16 -11
  103. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +29 -15
  104. data/lib/leftovers/matcher_builders/node_pair_key.rb +16 -0
  105. data/lib/leftovers/matcher_builders/node_privacy.rb +13 -0
  106. data/lib/leftovers/matcher_builders/node_type.rb +11 -11
  107. data/lib/leftovers/matcher_builders/node_value.rb +44 -25
  108. data/lib/leftovers/matcher_builders/or.rb +64 -49
  109. data/lib/leftovers/matcher_builders/path.rb +3 -1
  110. data/lib/leftovers/matcher_builders/string_pattern.rb +14 -5
  111. data/lib/leftovers/matcher_builders.rb +2 -1
  112. data/lib/leftovers/matchers/all.rb +4 -0
  113. data/lib/leftovers/matchers/and.rb +4 -0
  114. data/lib/leftovers/matchers/any.rb +2 -0
  115. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +7 -4
  116. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +5 -4
  117. data/lib/leftovers/matchers/node_has_positional_argument.rb +5 -1
  118. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +6 -1
  119. data/lib/leftovers/matchers/node_has_receiver.rb +4 -0
  120. data/lib/leftovers/matchers/node_is_proc.rb +13 -0
  121. data/lib/leftovers/matchers/node_name.rb +9 -3
  122. data/lib/leftovers/matchers/node_pair_key.rb +23 -0
  123. data/lib/leftovers/matchers/node_pair_value.rb +7 -3
  124. data/lib/leftovers/matchers/node_path.rb +7 -3
  125. data/lib/leftovers/matchers/node_privacy.rb +23 -0
  126. data/lib/leftovers/matchers/node_scalar_value.rb +6 -1
  127. data/lib/leftovers/matchers/node_type.rb +7 -3
  128. data/lib/leftovers/matchers/not.rb +2 -0
  129. data/lib/leftovers/matchers/or.rb +2 -0
  130. data/lib/leftovers/matchers/path.rb +21 -0
  131. data/lib/leftovers/matchers.rb +4 -1
  132. data/lib/leftovers/merged_config.rb +40 -75
  133. data/lib/leftovers/parser.rb +7 -4
  134. data/lib/leftovers/precompilers/erb.rb +22 -0
  135. data/lib/leftovers/precompilers/haml.rb +15 -0
  136. data/lib/leftovers/precompilers/json.rb +27 -0
  137. data/lib/leftovers/precompilers/precompiler.rb +28 -0
  138. data/lib/leftovers/precompilers/slim.rb +15 -0
  139. data/lib/leftovers/precompilers/yaml.rb +75 -0
  140. data/lib/leftovers/precompilers.rb +50 -0
  141. data/lib/leftovers/processor_builders/action.rb +69 -42
  142. data/lib/leftovers/processor_builders/add_prefix.rb +18 -10
  143. data/lib/leftovers/processor_builders/add_suffix.rb +18 -10
  144. data/lib/leftovers/processor_builders/argument.rb +28 -14
  145. data/lib/leftovers/processor_builders/dynamic.rb +57 -17
  146. data/lib/leftovers/processor_builders/each.rb +83 -11
  147. data/lib/leftovers/processor_builders/itself.rb +2 -2
  148. data/lib/leftovers/processor_builders/keyword.rb +9 -9
  149. data/lib/leftovers/processor_builders/keyword_argument.rb +4 -2
  150. data/lib/leftovers/processor_builders/receiver.rb +13 -0
  151. data/lib/leftovers/processor_builders/transform.rb +55 -44
  152. data/lib/leftovers/processor_builders/transform_chain.rb +16 -8
  153. data/lib/leftovers/processor_builders/transform_set.rb +16 -32
  154. data/lib/leftovers/processor_builders/value.rb +4 -4
  155. data/lib/leftovers/processor_builders.rb +1 -3
  156. data/lib/leftovers/processors/add_call.rb +14 -0
  157. data/lib/leftovers/processors/add_definition_node.rb +16 -0
  158. data/lib/leftovers/processors/add_dynamic_prefix.rb +29 -0
  159. data/lib/leftovers/processors/add_dynamic_suffix.rb +29 -0
  160. data/lib/leftovers/{value_processors → processors}/add_prefix.rb +7 -3
  161. data/lib/leftovers/{value_processors → processors}/add_suffix.rb +7 -3
  162. data/lib/leftovers/processors/append_sym.rb +13 -0
  163. data/lib/leftovers/{value_processors → processors}/camelize.rb +7 -3
  164. data/lib/leftovers/{value_processors → processors}/capitalize.rb +7 -3
  165. data/lib/leftovers/{value_processors → processors}/deconstantize.rb +7 -3
  166. data/lib/leftovers/{value_processors → processors}/delete_after.rb +9 -5
  167. data/lib/leftovers/processors/delete_after_last.rb +26 -0
  168. data/lib/leftovers/processors/delete_before.rb +27 -0
  169. data/lib/leftovers/processors/delete_before_last.rb +27 -0
  170. data/lib/leftovers/{value_processors → processors}/delete_prefix.rb +7 -3
  171. data/lib/leftovers/{value_processors → processors}/delete_suffix.rb +7 -3
  172. data/lib/leftovers/{value_processors → processors}/demodulize.rb +7 -3
  173. data/lib/leftovers/{value_processors → processors}/downcase.rb +7 -3
  174. data/lib/leftovers/processors/each.rb +25 -0
  175. data/lib/leftovers/processors/each_for_definition_set.rb +33 -0
  176. data/lib/leftovers/processors/each_keyword.rb +29 -0
  177. data/lib/leftovers/processors/each_keyword_argument.rb +29 -0
  178. data/lib/leftovers/processors/each_positional_argument.rb +27 -0
  179. data/lib/leftovers/processors/each_positional_argument_from.rb +30 -0
  180. data/lib/leftovers/processors/eval.rb +16 -0
  181. data/lib/leftovers/processors/itself.rb +21 -0
  182. data/lib/leftovers/{value_processors → processors}/keyword_argument.rb +9 -11
  183. data/lib/leftovers/processors/match_current_node.rb +26 -0
  184. data/lib/leftovers/processors/match_matched_node.rb +26 -0
  185. data/lib/leftovers/{value_processors → processors}/parameterize.rb +7 -3
  186. data/lib/leftovers/{value_processors → processors}/placeholder.rb +5 -4
  187. data/lib/leftovers/{value_processors → processors}/pluralize.rb +7 -3
  188. data/lib/leftovers/{value_processors → processors}/positional_argument.rb +8 -6
  189. data/lib/leftovers/processors/receiver.rb +24 -0
  190. data/lib/leftovers/{value_processors → processors}/replace_value.rb +7 -3
  191. data/lib/leftovers/processors/set_default_privacy.rb +21 -0
  192. data/lib/leftovers/processors/set_privacy.rb +23 -0
  193. data/lib/leftovers/{value_processors → processors}/singularize.rb +7 -3
  194. data/lib/leftovers/{value_processors → processors}/split.rb +8 -4
  195. data/lib/leftovers/{value_processors → processors}/swapcase.rb +7 -3
  196. data/lib/leftovers/{value_processors → processors}/titleize.rb +7 -3
  197. data/lib/leftovers/{value_processors → processors}/underscore.rb +7 -3
  198. data/lib/leftovers/{value_processors → processors}/upcase.rb +7 -3
  199. data/lib/leftovers/processors.rb +49 -0
  200. data/lib/leftovers/rake_task.rb +1 -1
  201. data/lib/leftovers/version.rb +1 -1
  202. data/lib/leftovers.rb +55 -20
  203. metadata +159 -72
  204. data/lib/leftovers/config_validator/error_processor.rb +0 -196
  205. data/lib/leftovers/config_validator/schema_hash.rb +0 -551
  206. data/lib/leftovers/config_validator.rb +0 -61
  207. data/lib/leftovers/dynamic_processors/call.rb +0 -21
  208. data/lib/leftovers/dynamic_processors/call_definition.rb +0 -27
  209. data/lib/leftovers/dynamic_processors/definition.rb +0 -22
  210. data/lib/leftovers/dynamic_processors/each.rb +0 -19
  211. data/lib/leftovers/dynamic_processors/null.rb +0 -9
  212. data/lib/leftovers/dynamic_processors.rb +0 -11
  213. data/lib/leftovers/erb.rb +0 -20
  214. data/lib/leftovers/haml.rb +0 -21
  215. data/lib/leftovers/json.rb +0 -28
  216. data/lib/leftovers/matcher_builders/node_pair_name.rb +0 -18
  217. data/lib/leftovers/matchers/predicate.rb +0 -19
  218. data/lib/leftovers/processor_builders/each_action.rb +0 -51
  219. data/lib/leftovers/processor_builders/each_dynamic.rb +0 -54
  220. data/lib/leftovers/processor_builders/each_for_definition_set.rb +0 -36
  221. data/lib/leftovers/slim.rb +0 -21
  222. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +0 -31
  223. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +0 -31
  224. data/lib/leftovers/value_processors/delete_before.rb +0 -22
  225. data/lib/leftovers/value_processors/each.rb +0 -21
  226. data/lib/leftovers/value_processors/each_for_definition_set.rb +0 -29
  227. data/lib/leftovers/value_processors/each_keyword.rb +0 -27
  228. data/lib/leftovers/value_processors/each_keyword_argument.rb +0 -27
  229. data/lib/leftovers/value_processors/each_positional_argument.rb +0 -24
  230. data/lib/leftovers/value_processors/itself.rb +0 -17
  231. data/lib/leftovers/value_processors/keyword.rb +0 -32
  232. data/lib/leftovers/value_processors/return_definition.rb +0 -22
  233. data/lib/leftovers/value_processors/return_string.rb +0 -14
  234. data/lib/leftovers/value_processors.rb +0 -40
  235. data/lib/leftovers/yaml.rb +0 -73
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class ArraySchema < Schema
6
+ class << self
7
+ def [](value_schema)
8
+ new(value_schema)
9
+ end
10
+ end
11
+
12
+ attr_reader :value_schema
13
+
14
+ def initialize(value_schema)
15
+ @value_schema = value_schema
16
+
17
+ super()
18
+ end
19
+
20
+ def validate(node)
21
+ validate_array(node) && validate_length(node) && validate_values(node)
22
+ end
23
+
24
+ def to_ruby(node)
25
+ node.children.map do |value|
26
+ value_schema.to_ruby(value)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def validate_array(node)
33
+ self.class.error(node, 'be an array') unless node.array?
34
+
35
+ node.valid?
36
+ end
37
+
38
+ def validate_length(node)
39
+ self.class.error(node, 'not be empty') if node.children.empty?
40
+
41
+ node.valid?
42
+ end
43
+
44
+ def validate_values(node)
45
+ node.children.each do |value|
46
+ value_schema.validate(value)
47
+ end
48
+
49
+ node.children.all?(&:valid?)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class Attribute
6
+ attr_reader(
7
+ :name,
8
+ :aliases, :schema # leftovers:test_only
9
+ )
10
+
11
+ attr_accessor :require_group
12
+
13
+ def initialize(name, schema, aliases: nil, require_group: nil, suggest: true)
14
+ @name = name
15
+ @schema = schema
16
+ @aliases = aliases
17
+ @require_group = require_group
18
+ @suggest = suggest
19
+ end
20
+
21
+ def without_require_group
22
+ new = dup
23
+ new.require_group = nil
24
+ new
25
+ end
26
+
27
+ def suggest?
28
+ @suggest
29
+ end
30
+
31
+ def attributes
32
+ [self]
33
+ end
34
+
35
+ def name?(name)
36
+ name = name.to_sym
37
+
38
+ @name == name || Leftovers.each_or_self(@aliases).include?(name)
39
+ end
40
+
41
+ def to_ruby(value)
42
+ [key_to_ruby, @schema.to_ruby(value)]
43
+ end
44
+
45
+ def validate_value(value)
46
+ @schema.validate(value)
47
+ end
48
+
49
+ private
50
+
51
+ def key_to_ruby
52
+ name == :unless ? :unless_arg : name
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class BuiltInPrecompilerSchema < StringEnumSchema
6
+ value :erb
7
+ value :yaml
8
+ value :json
9
+ value :slim
10
+ value :haml
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class DocumentSchema < ObjectSchema
6
+ attribute :include_paths, ValueOrArraySchema[StringSchema], aliases: :include_path
7
+ attribute :exclude_paths, ValueOrArraySchema[StringSchema], aliases: :exclude_path
8
+ attribute :test_paths, ValueOrArraySchema[StringSchema], aliases: :test_path
9
+ attribute :haml_paths, ValueOrArraySchema[StringSchema], aliases: :haml_path, suggest: false
10
+ attribute :slim_paths, ValueOrArraySchema[StringSchema], aliases: :slim_path, suggest: false
11
+ attribute :yaml_paths, ValueOrArraySchema[StringSchema], aliases: :yaml_path, suggest: false
12
+ attribute :json_paths, ValueOrArraySchema[StringSchema], aliases: :json_path, suggest: false
13
+ attribute :erb_paths, ValueOrArraySchema[StringSchema], aliases: :erb_path, suggest: false
14
+ attribute :precompile, ValueOrArraySchema[PrecompileSchema]
15
+ attribute :requires, ValueOrArraySchema[RequireSchema], aliases: :require
16
+ attribute :gems, ValueOrArraySchema[StringSchema], aliases: :gem
17
+ attribute :keep, ValueOrArraySchema[KeepTestOnlySchema]
18
+ attribute :test_only, ValueOrArraySchema[KeepTestOnlySchema]
19
+ attribute :dynamic, ValueOrArraySchema[DynamicSchema]
20
+
21
+ PRECOMPILERS = %i{haml_paths slim_paths json_paths yaml_paths erb_paths}.freeze
22
+
23
+ def self.to_ruby(node) # rubocop:disable Metrics
24
+ read_hash = super
25
+ write_hash = read_hash.dup
26
+
27
+ read_hash.each do |key, value|
28
+ next unless PRECOMPILERS.include?(key)
29
+
30
+ value = { paths: value, format: key.to_s.delete_suffix('_paths').to_sym }
31
+ yaml = { 'precompile' => [value.transform_keys(&:to_s).transform_values(&:to_s)] }
32
+ .to_yaml.delete_prefix("---\n")
33
+
34
+ Leftovers.warn(<<~MESSAGE)
35
+ \e[33m`#{key}:` is deprecated\e[0m
36
+ Replace with:
37
+ \e[32m#{yaml}\e[0m
38
+ MESSAGE
39
+
40
+ write_hash[:precompile] = Leftovers.each_or_self(write_hash[:precompile]).to_a
41
+ write_hash[:precompile] << value
42
+ write_hash.delete(key)
43
+ end
44
+
45
+ write_hash
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class DynamicSchema < ObjectSchema
6
+ inherit_attributes_from RulePatternSchema
7
+
8
+ attribute :call, ValueOrArraySchema[ValueProcessorSchema], aliases: :calls,
9
+ require_group: :processor
10
+ attribute :define, ValueOrArraySchema[ValueProcessorSchema], aliases: :defines,
11
+ require_group: :processor
12
+ attribute :set_privacy, ValueOrArraySchema[PrivacyProcessorSchema],
13
+ require_group: :processor
14
+ attribute :set_default_privacy, PrivacySchema, require_group: :processor
15
+ attribute :eval, ValueOrArraySchema[ValueProcessorSchema], require_group: :processor
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class HasArgumentSchema < ValueOrObjectSchema
6
+ attribute :at, ValueOrArraySchema[ArgumentPositionSchema], require_group: :matcher
7
+ attribute :has_value, ValueOrArraySchema[HasValueSchema], require_group: :matcher
8
+ attribute :unless, ValueOrArraySchema[HasArgumentSchema], require_group: :matcher
9
+
10
+ self.or_value_schema = ScalarArgumentSchema
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class HasValueSchema < ValueOrObjectSchema
6
+ attribute :names, ValueOrArraySchema[StringPatternSchema], aliases: :name,
7
+ require_group: :matcher
8
+ inherit_attributes_from StringPatternSchema, except: :unless
9
+ attribute :has_arguments, ValueOrArraySchema[HasArgumentSchema], aliases: :has_argument,
10
+ require_group: :matcher
11
+
12
+ attribute :at, ValueOrArraySchema[ArgumentPositionSchema], require_group: :matcher
13
+ attribute :has_value, ValueOrArraySchema[HasValueSchema], require_group: :matcher
14
+
15
+ attribute :has_receiver, ValueOrArraySchema[HasValueSchema], require_group: :matcher
16
+ attribute :type, ValueOrArraySchema[ValueTypeSchema], require_group: :matcher
17
+ attribute :unless, ValueOrArraySchema[HasValueSchema], require_group: :matcher
18
+
19
+ self.or_value_schema = ScalarValueSchema
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class InheritSchemaAttributes
6
+ def initialize(schema, require_group: true, except: nil)
7
+ @schema = schema
8
+ @use_require_groups = require_group
9
+ @except = Leftovers.each_or_self(except)
10
+ end
11
+
12
+ def attributes
13
+ @schema.attributes.map do |attr|
14
+ next if @except.include?(attr.name)
15
+ next attr.without_require_group unless @use_require_groups
16
+
17
+ attr
18
+ end.compact
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class KeepTestOnlySchema < ValueOrObjectSchema
6
+ inherit_attributes_from StringPatternSchema, except: :unless
7
+ inherit_attributes_from RulePatternSchema, except: :unless
8
+ attribute :unless, ValueOrArraySchema[KeepTestOnlySchema], require_group: :matcher
9
+
10
+ self.or_value_schema = StringSchema
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class KeywordArgumentSchema < ValueOrObjectSchema
6
+ inherit_attributes_from StringPatternSchema, except: :unless
7
+ attribute :type, ValueOrArraySchema[ValueTypeSchema], require_group: :matcher
8
+ attribute :unless, ValueOrArraySchema[KeywordArgumentSchema], require_group: :matcher
9
+
10
+ self.or_value_schema = StringSchema
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class Node
6
+ class_loader = Psych::ClassLoader::Restricted.new([], [])
7
+ ToRuby = Psych::Visitors::ToRuby.new(
8
+ Psych::ScalarScanner.new(class_loader),
9
+ class_loader
10
+ )
11
+
12
+ attr_reader :node, :file, :name
13
+ attr_accessor :error
14
+
15
+ def initialize(node, file, name = nil)
16
+ @node = node
17
+ @file = file
18
+ @name = name
19
+ end
20
+
21
+ def name_
22
+ "#{name} " if name
23
+ end
24
+
25
+ def valid?
26
+ !error
27
+ end
28
+
29
+ def error_message
30
+ "Config SchemaError: #{location} #{@error}" if @error
31
+ end
32
+
33
+ def all_errors
34
+ Array(error_message) + children.flat_map(&:all_errors)
35
+ end
36
+
37
+ def to_s
38
+ to_ruby.to_s
39
+ end
40
+
41
+ def hash?
42
+ node.is_a?(Psych::Nodes::Mapping)
43
+ end
44
+
45
+ def location
46
+ "#{file.relative_path}:#{node.start_line + 1}:#{node.start_column}"
47
+ end
48
+
49
+ def children
50
+ @children ||= if hash?
51
+ prepare_hash_children
52
+ elsif array?
53
+ node.children.map { |value| self.class.new(value, file, "#{name} value") }
54
+ else
55
+ []
56
+ end
57
+ end
58
+
59
+ def keys
60
+ @keys ||= pairs.map(&:first)
61
+ end
62
+
63
+ def pairs
64
+ @pairs ||= children.each_slice(2).to_a
65
+ end
66
+
67
+ def each_key(&block)
68
+ keys.each(&block)
69
+ end
70
+
71
+ def to_ruby
72
+ @to_ruby ||= ToRuby.accept(node)
73
+ end
74
+
75
+ def to_sym
76
+ to_ruby.to_sym if string?
77
+ end
78
+
79
+ def string?
80
+ to_ruby.is_a?(String)
81
+ end
82
+
83
+ def scalar?
84
+ !array? and !hash?
85
+ end
86
+
87
+ def array?
88
+ node.is_a?(Psych::Nodes::Sequence)
89
+ end
90
+
91
+ def integer?
92
+ to_ruby.is_a?(Integer)
93
+ end
94
+
95
+ private
96
+
97
+ def prepare_hash_children
98
+ node.children.each_slice(2).flat_map do |key, value|
99
+ key = self.class.new(key, file, name)
100
+ value = self.class.new(value, file, key.to_ruby)
101
+ [key, value]
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class ObjectSchema < Schema
6
+ class << self
7
+ def inherit_attributes_from(schema, require_group: true, except: nil)
8
+ attributes_and_schemas_to_inherit_from << InheritSchemaAttributes.new(
9
+ schema, require_group: require_group, except: except
10
+ )
11
+ end
12
+
13
+ def attributes
14
+ attributes_and_schemas_to_inherit_from.flat_map(&:attributes)
15
+ end
16
+
17
+ def attribute(name, value_schema, aliases: nil, require_group: nil, suggest: true)
18
+ attributes_and_schemas_to_inherit_from << Attribute.new(
19
+ name, value_schema,
20
+ aliases: aliases, require_group: require_group, suggest: suggest
21
+ )
22
+ end
23
+
24
+ def require_groups
25
+ attributes.group_by(&:require_group).tap { |rg| rg.delete(nil) }.values
26
+ end
27
+
28
+ def validate(node)
29
+ if node.hash?
30
+ validate_attributes(node)
31
+ else
32
+ error(node, 'be a hash')
33
+
34
+ node.valid?
35
+ end
36
+ end
37
+
38
+ def to_ruby(node)
39
+ node.pairs.map { |(key, value)| attribute_for_key(key).to_ruby(value) }.to_h
40
+ end
41
+
42
+ private
43
+
44
+ def attributes_and_schemas_to_inherit_from
45
+ @attributes_and_schemas_to_inherit_from ||= []
46
+ end
47
+
48
+ def validate_attributes(node)
49
+ validate_attribute_keys(node) && validate_required_keys(node)
50
+ validate_alias_uniqueness_of_keys(node)
51
+ validate_valid_attribute_values(node)
52
+
53
+ node.children.all?(&:valid?)
54
+ end
55
+
56
+ def attribute_for_key(node)
57
+ attributes.find { |attr| attr.name?(node) }
58
+ end
59
+
60
+ def validate_attribute_keys(node)
61
+ node.each_key { |key| validate_recognized_key(key, node) }
62
+ node.keys.all?(&:valid?)
63
+ end
64
+
65
+ def validate_alias_uniqueness_of_keys(node)
66
+ node.keys.select(&:valid?)
67
+ .group_by { |key| attribute_for_key(key) }
68
+ .each_value { |keys| error_message_for_non_unique_keys(node, keys) if keys.length > 1 }
69
+
70
+ node.valid?
71
+ end
72
+
73
+ def error_message_for_non_unique_keys(node, keys)
74
+ keys.each { |k| k.error = "#{node.name_}must only use one of #{keys.uniq.join(' or ')}" }
75
+ end
76
+
77
+ def validate_recognized_key(key, node)
78
+ return true if attribute_for_key(key)
79
+
80
+ suggestions = suggestions_for_unrecognized_key(key, node)
81
+ did_you_mean = "\nDid you mean: #{suggestions.join(', ')}" unless suggestions.empty?
82
+ key.error = "unrecognized key #{key}#{" for #{node.name}" if node.name}#{did_you_mean}"
83
+
84
+ false
85
+ end
86
+
87
+ def suggester
88
+ @suggester ||= Suggester.new(suggestions)
89
+ end
90
+
91
+ def suggestions_for_unrecognized_key(key, node)
92
+ suggester.suggest(key.to_ruby) - node.keys.map { |k| attribute_for_key(k)&.name }.compact
93
+ end
94
+
95
+ def suggestions(attributes = self.attributes)
96
+ attributes.select(&:suggest?).map(&:name)
97
+ end
98
+
99
+ def validate_required_keys(node)
100
+ missing_groups = require_groups.map do |group|
101
+ next if node.keys.any? { |key| group.any? { |attr| attr.name?(key) } }
102
+
103
+ "include at least one of #{suggestions(group).join(', ')}"
104
+ end.compact
105
+
106
+ error(node, missing_groups.join(' and ')) unless missing_groups.empty?
107
+
108
+ node.valid?
109
+ end
110
+
111
+ def validate_valid_attribute_values(node)
112
+ node.pairs.each { |(k, v)| attribute_for_key(k).validate_value(v) if k.valid? }
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class PrecompileSchema < ObjectSchema
6
+ attribute :paths, ValueOrArraySchema[StringSchema],
7
+ aliases: %i{path include_paths include_path}, require_group: :paths
8
+
9
+ attribute :format, PrecompilerSchema, require_group: :precompiler
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class PrecompilerSchema < ValueOrObjectSchema
6
+ attribute :custom, StringSchema, require_group: :custom
7
+
8
+ self.or_value_schema = BuiltInPrecompilerSchema
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class PrivacyProcessorSchema < ObjectSchema
6
+ inherit_attributes_from ValueProcessorSchema
7
+ attribute :to, PrivacySchema, require_group: :privacy_setting
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class PrivacySchema < StringEnumSchema
6
+ value :public
7
+ value :protected
8
+ value :private
9
+
10
+ def self.to_ruby(node)
11
+ super.to_sym
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class RegexpSchema < StringSchema
6
+ class << self
7
+ def validate(node)
8
+ validate_string(node) && validate_regexp(node)
9
+ end
10
+
11
+ def validate_string(node)
12
+ error(node, 'be a string with a valid ruby regexp') unless node.string?
13
+
14
+ node.valid?
15
+ end
16
+
17
+ def validate_regexp(node)
18
+ /#{node.to_ruby}/
19
+ rescue RegexpError, ArgumentError => e
20
+ error(node, "be a string with a valid ruby regexp (#{e.message})")
21
+ else
22
+ true
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class RequireSchema < ValueOrObjectSchema
6
+ attribute :quiet, StringSchema, require_group: :quiet
7
+
8
+ self.or_value_schema = StringSchema
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class RulePatternSchema < ObjectSchema
6
+ attribute :names, ValueOrArraySchema[StringPatternSchema], aliases: :name,
7
+ require_group: :matcher
8
+ attribute :paths, ValueOrArraySchema[StringSchema], aliases: :path, require_group: :matcher
9
+ attribute :document, TrueSchema, require_group: :matcher
10
+ attribute :has_arguments, ValueOrArraySchema[HasArgumentSchema], aliases: :has_argument,
11
+ require_group: :matcher
12
+ attribute :has_receiver, ValueOrArraySchema[HasValueSchema], require_group: :matcher
13
+ attribute :type, ValueOrArraySchema[ValueTypeSchema], require_group: :matcher
14
+ attribute :privacy, ValueOrArraySchema[PrivacySchema], require_group: :matcher
15
+ attribute :unless, ValueOrArraySchema[RulePatternSchema], require_group: :matcher
16
+ attribute :all, ArraySchema[RulePatternSchema], require_group: :matcher
17
+ attribute :any, ArraySchema[RulePatternSchema], require_group: :matcher
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class ScalarArgumentSchema < Schema
6
+ class << self
7
+ def validate(node)
8
+ error(node, 'be a string or an integer') unless node.string? || node.integer?
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ class ConfigLoader
5
+ class ScalarValueSchema < Schema
6
+ class << self
7
+ def validate(node)
8
+ error(node, 'be any scalar value') unless node.scalar?
9
+ super
10
+ end
11
+
12
+ def to_ruby(node)
13
+ if node.to_ruby.nil?
14
+ :_leftovers_nil_value
15
+ else
16
+ node.to_ruby
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end