leftovers 0.6.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +21 -3
- data/docs/Configuration.md +82 -18
- data/leftovers.gemspec +1 -1
- data/lib/config/actionmailer.yml +11 -11
- data/lib/config/activesupport.yml +1 -1
- data/lib/config/rails.yml +1 -1
- data/lib/config/railties.yml +11 -0
- data/lib/config/ruby.yml +72 -0
- data/lib/leftovers/ast/node.rb +16 -11
- data/lib/leftovers/config.rb +1 -24
- data/lib/leftovers/config_loader/argument_position_schema.rb +11 -0
- data/lib/leftovers/config_loader/argumentless_transform_schema.rb +21 -0
- data/lib/leftovers/config_loader/attribute.rb +30 -0
- data/lib/leftovers/config_loader/document_schema.rb +21 -0
- data/lib/leftovers/config_loader/dynamic_schema.rb +17 -0
- data/lib/leftovers/config_loader/has_argument_schema.rb +13 -0
- data/lib/leftovers/config_loader/has_value_schema.rb +18 -0
- data/lib/leftovers/config_loader/keep_test_only_schema.rb +13 -0
- data/lib/leftovers/config_loader/node.rb +106 -0
- data/lib/leftovers/config_loader/object_schema.rb +189 -0
- data/lib/leftovers/config_loader/privacy_processor_schema.rb +12 -0
- data/lib/leftovers/config_loader/privacy_schema.rb +15 -0
- data/lib/leftovers/config_loader/require_schema.rb +11 -0
- data/lib/leftovers/config_loader/rule_pattern_schema.rb +18 -0
- data/lib/leftovers/config_loader/scalar_argument_schema.rb +14 -0
- data/lib/leftovers/config_loader/scalar_value_schema.rb +14 -0
- data/lib/leftovers/config_loader/schema.rb +21 -0
- data/lib/leftovers/config_loader/string_enum_schema.rb +62 -0
- data/lib/leftovers/config_loader/string_pattern_schema.rb +14 -0
- data/lib/leftovers/config_loader/string_schema.rb +14 -0
- data/lib/leftovers/config_loader/string_value_processor_schema.rb +11 -0
- data/lib/leftovers/config_loader/suggester.rb +22 -0
- data/lib/leftovers/config_loader/transform_schema.rb +28 -0
- data/lib/leftovers/config_loader/true_schema.rb +18 -0
- data/lib/leftovers/config_loader/value_matcher_schema.rb +18 -0
- data/lib/leftovers/config_loader/value_or_array_schema.rb +64 -0
- data/lib/leftovers/config_loader/value_processor_schema.rb +14 -0
- data/lib/leftovers/config_loader/value_type_schema.rb +17 -0
- data/lib/leftovers/config_loader.rb +82 -0
- data/lib/leftovers/definition_node.rb +6 -17
- data/lib/leftovers/definition_node_set.rb +11 -0
- data/lib/leftovers/definition_to_add.rb +31 -0
- data/lib/leftovers/dynamic_processors/call.rb +4 -3
- data/lib/leftovers/dynamic_processors/call_definition.rb +14 -7
- data/lib/leftovers/dynamic_processors/definition.rb +8 -3
- data/lib/leftovers/dynamic_processors/set_default_privacy.rb +18 -0
- data/lib/leftovers/dynamic_processors/set_privacy.rb +23 -0
- data/lib/leftovers/dynamic_processors.rb +2 -0
- data/lib/leftovers/file.rb +5 -1
- data/lib/leftovers/file_collector.rb +44 -17
- data/lib/leftovers/matcher_builders/node.rb +4 -0
- data/lib/leftovers/matcher_builders/node_privacy.rb +13 -0
- data/lib/leftovers/matcher_builders/node_type.rb +4 -4
- data/lib/leftovers/matcher_builders/node_value.rb +1 -1
- data/lib/leftovers/matcher_builders/string_pattern.rb +14 -5
- data/lib/leftovers/matcher_builders.rb +1 -0
- data/lib/leftovers/matchers/node_privacy.rb +19 -0
- data/lib/leftovers/matchers.rb +1 -0
- data/lib/leftovers/merged_config.rb +18 -34
- data/lib/leftovers/processor_builders/add_prefix.rb +1 -1
- data/lib/leftovers/processor_builders/add_suffix.rb +1 -1
- data/lib/leftovers/processor_builders/dynamic.rb +50 -16
- data/lib/leftovers/processor_builders/transform.rb +2 -2
- data/lib/leftovers/processor_builders/transform_set.rb +8 -8
- data/lib/leftovers/value_processors/each_for_definition_set.rb +2 -5
- data/lib/leftovers/value_processors/each_positional_argument.rb +1 -1
- data/lib/leftovers/value_processors/return_definition_node.rb +14 -0
- data/lib/leftovers/value_processors/{return_string.rb → return_sym.rb} +1 -1
- data/lib/leftovers/value_processors.rb +2 -2
- data/lib/leftovers/version.rb +1 -1
- data/lib/leftovers.rb +23 -14
- metadata +54 -22
- data/lib/config/actioncable.yml +0 -4
- data/lib/leftovers/config_validator/error_processor.rb +0 -196
- data/lib/leftovers/config_validator/schema_hash.rb +0 -551
- data/lib/leftovers/config_validator.rb +0 -61
- data/lib/leftovers/value_processors/return_definition.rb +0 -22
@@ -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,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class ObjectSchema < Schema # rubocop:disable Metrics/ClassLength
|
6
|
+
class << self
|
7
|
+
def inherit_attributes_from(schema, require_group: true, except: nil)
|
8
|
+
attributes_and_schemas_to_inherit_from << schema
|
9
|
+
|
10
|
+
inherit_except[schema] = Leftovers.each_or_self(except)
|
11
|
+
|
12
|
+
return if require_group
|
13
|
+
|
14
|
+
skip_require_group << schema
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes
|
18
|
+
nonexcluded_attributes = attributes_and_schemas_to_inherit_from.map do |attr_or_schema|
|
19
|
+
attr_or_schema.attributes.dup.tap do |attributes_copy|
|
20
|
+
inherit_except[attr_or_schema]&.each { |e| attributes_copy.delete(e) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
nonexcluded_attributes.reduce { |a, e| a.merge(e) { raise 'Duplicate attributes' } }
|
25
|
+
end
|
26
|
+
|
27
|
+
def aliases
|
28
|
+
attributes_and_schemas_to_inherit_from.map(&:aliases)
|
29
|
+
.reduce { |a, e| a.merge(e) { raise 'Duplicate aliases' } }
|
30
|
+
end
|
31
|
+
|
32
|
+
def require_groups
|
33
|
+
(attributes_and_schemas_to_inherit_from - skip_require_group)
|
34
|
+
.map(&:require_groups).each_with_object({}) do |require_groups, hash|
|
35
|
+
require_groups.each do |group, keys|
|
36
|
+
hash[group] ||= []
|
37
|
+
hash[group] += keys
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :or_schema
|
43
|
+
|
44
|
+
def attribute(name, value_schema, aliases: nil, require_group: nil)
|
45
|
+
attributes_and_schemas_to_inherit_from << Attribute.new(
|
46
|
+
name,
|
47
|
+
value_schema,
|
48
|
+
aliases: aliases,
|
49
|
+
require_group: require_group
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate(node)
|
54
|
+
if node.hash?
|
55
|
+
validate_attributes(node)
|
56
|
+
elsif or_schema
|
57
|
+
validate_or_schema(node)
|
58
|
+
else
|
59
|
+
validate_is_hash(node)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_ruby(node)
|
64
|
+
if node.hash?
|
65
|
+
node.pairs.map { |(key, value)| pair_to_ruby(key, value) }.to_h
|
66
|
+
else
|
67
|
+
or_schema.to_ruby(node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def inherit_except
|
74
|
+
@inherit_except ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def skip_require_group
|
78
|
+
@skip_require_group ||= []
|
79
|
+
end
|
80
|
+
|
81
|
+
def attributes_and_schemas_to_inherit_from
|
82
|
+
@attributes_and_schemas_to_inherit_from ||= []
|
83
|
+
end
|
84
|
+
|
85
|
+
def pair_to_ruby(key, value)
|
86
|
+
key_sym = key.to_sym
|
87
|
+
key_sym = aliases[key_sym] || key_sym
|
88
|
+
key_sym = :unless_arg if key_sym == :unless
|
89
|
+
[key_sym, schema_for_attribute(key).to_ruby(value)]
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_attributes(node)
|
93
|
+
validate_attribute_keys(node) && validate_required_keys(node)
|
94
|
+
validate_alias_uniqueness_of_keys(node)
|
95
|
+
validate_valid_attribute_values(node)
|
96
|
+
|
97
|
+
node.children.all?(&:valid?)
|
98
|
+
end
|
99
|
+
|
100
|
+
def validate_or_schema(node)
|
101
|
+
or_schema.validate(node)
|
102
|
+
return true if node.valid?
|
103
|
+
|
104
|
+
if node.string? && valid_keys.include?(node.to_sym)
|
105
|
+
node.error = "#{node.name_}#{node.to_sym} must be a hash key"
|
106
|
+
else
|
107
|
+
node.error += " or a hash with any of #{attributes.keys.join(', ')}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_is_hash(node)
|
112
|
+
error(node, 'be a hash')
|
113
|
+
node.valid?
|
114
|
+
end
|
115
|
+
|
116
|
+
def validate_attribute_keys(node)
|
117
|
+
node.each_key { |key| validate_recognized_key(key, node) }
|
118
|
+
node.keys.all?(&:valid?)
|
119
|
+
end
|
120
|
+
|
121
|
+
def valid_keys
|
122
|
+
attributes.keys + aliases.keys
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_alias_uniqueness_of_keys(node)
|
126
|
+
node.keys.select(&:valid?)
|
127
|
+
.group_by { |key| aliases[key.to_sym] || key.to_sym }
|
128
|
+
.each_value do |keys|
|
129
|
+
next unless keys.length > 1
|
130
|
+
|
131
|
+
error_message_for_non_unique_keys(node, keys)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def error_message_for_non_unique_keys(node, keys)
|
136
|
+
keys.each do |key|
|
137
|
+
key.error = "#{node.name_}must only use one of #{keys.uniq.join(' or ')}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def validate_recognized_key(key, node)
|
142
|
+
return true if valid_keys.include?(key.to_sym)
|
143
|
+
|
144
|
+
suggestions = suggestions_for_unrecognized_key(key, node)
|
145
|
+
did_you_mean = "\nDid you mean: #{suggestions.join(', ')}" unless suggestions.empty?
|
146
|
+
for_name = " for #{node.name}" if node.name
|
147
|
+
|
148
|
+
key.error = "unrecognized key #{key}#{for_name}#{did_you_mean}"
|
149
|
+
|
150
|
+
false
|
151
|
+
end
|
152
|
+
|
153
|
+
def suggester
|
154
|
+
@suggester ||= Suggester.new(attributes.keys)
|
155
|
+
end
|
156
|
+
|
157
|
+
def suggestions_for_unrecognized_key(key, node)
|
158
|
+
existing_keys = node.keys.flat_map { |k| [k.to_sym, aliases[k.to_sym]] }.compact
|
159
|
+
suggester.suggest(key.to_ruby) - existing_keys
|
160
|
+
end
|
161
|
+
|
162
|
+
def validate_required_keys(node)
|
163
|
+
missing_groups = require_groups.map do |_name, group|
|
164
|
+
next if node.keys.any? { |key| group.include?(key.to_sym) }
|
165
|
+
|
166
|
+
"include at least one of #{(attributes.keys & group).join(', ')}"
|
167
|
+
end.compact
|
168
|
+
|
169
|
+
error(node, missing_groups.join(' and ')) unless missing_groups.empty?
|
170
|
+
|
171
|
+
node.valid?
|
172
|
+
end
|
173
|
+
|
174
|
+
def schema_for_attribute(key)
|
175
|
+
key_sym = key.to_sym
|
176
|
+
attributes[key_sym] || attributes.fetch(aliases[key_sym])
|
177
|
+
end
|
178
|
+
|
179
|
+
def validate_valid_attribute_values(node)
|
180
|
+
node.pairs.each do |(key, value)|
|
181
|
+
next unless key.valid?
|
182
|
+
|
183
|
+
schema_for_attribute(key).validate(value)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
+
|
9
|
+
self.or_schema = nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
+
end
|
17
|
+
end
|
18
|
+
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,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class Schema
|
6
|
+
class << self
|
7
|
+
def error(node, requirement)
|
8
|
+
node.error = "#{node.name_}must #{requirement}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(node)
|
12
|
+
node.valid?
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_ruby(node)
|
16
|
+
node.to_ruby
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class StringEnumSchema < Schema
|
6
|
+
class << self
|
7
|
+
def value(value, aliases: nil)
|
8
|
+
values << value
|
9
|
+
Leftovers.each_or_self(aliases) do |alias_name|
|
10
|
+
self.aliases[alias_name] = value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def aliases
|
15
|
+
@aliases ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def aliases_for(value)
|
19
|
+
aliases.select { |_k, v| v == value }.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def values
|
23
|
+
@values ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_value(&block)
|
27
|
+
@values.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_ruby(node)
|
31
|
+
aliases[node.to_sym]&.to_s || node.to_ruby
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate(node)
|
35
|
+
if node.string?
|
36
|
+
node.error = error_message_with_suggestions(node) unless valid_value?(node.to_sym)
|
37
|
+
else
|
38
|
+
error(node, 'be a string')
|
39
|
+
end
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def valid_value?(val)
|
47
|
+
values.include?(val) || aliases.key?(val)
|
48
|
+
end
|
49
|
+
|
50
|
+
def suggester
|
51
|
+
@suggester ||= Suggester.new(values)
|
52
|
+
end
|
53
|
+
|
54
|
+
def error_message_with_suggestions(node)
|
55
|
+
suggestions = suggester.suggest(node.to_ruby)
|
56
|
+
|
57
|
+
"unrecognized value #{node} for #{node.name}\nDid you mean: #{suggestions.join(', ')}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class StringPatternSchema < ObjectSchema
|
6
|
+
attribute :match, StringSchema, aliases: :matches, require_group: :matcher
|
7
|
+
attribute :has_prefix, StringSchema, require_group: :matcher
|
8
|
+
attribute :has_suffix, StringSchema, require_group: :matcher
|
9
|
+
attribute :unless, ValueOrArraySchema[StringPatternSchema], require_group: :matcher
|
10
|
+
|
11
|
+
self.or_schema = StringSchema
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class Suggester
|
6
|
+
def initialize(words)
|
7
|
+
@words = words
|
8
|
+
@did_you_mean = ::DidYouMean::SpellChecker.new(dictionary: words) if defined?(::DidYouMean)
|
9
|
+
end
|
10
|
+
|
11
|
+
def suggest(word)
|
12
|
+
suggestions = did_you_mean.correct(word) if did_you_mean
|
13
|
+
suggestions = words if !suggestions || suggestions.empty?
|
14
|
+
suggestions
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :words, :did_you_mean
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class TransformSchema < ObjectSchema
|
6
|
+
ArgumentlessTransformSchema.each_value do |transform|
|
7
|
+
attribute(
|
8
|
+
transform, TrueSchema,
|
9
|
+
aliases: ArgumentlessTransformSchema.aliases_for(transform),
|
10
|
+
require_group: :processor
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
attribute :add_prefix, ValueOrArraySchema[StringValueProcessorSchema],
|
15
|
+
require_group: :processor
|
16
|
+
attribute :add_suffix, ValueOrArraySchema[StringValueProcessorSchema],
|
17
|
+
require_group: :processor
|
18
|
+
|
19
|
+
attribute :split, StringSchema, require_group: :processor
|
20
|
+
attribute :delete_prefix, StringSchema, require_group: :processor
|
21
|
+
attribute :delete_suffix, StringSchema, require_group: :processor
|
22
|
+
attribute :delete_before, StringSchema, require_group: :processor
|
23
|
+
attribute :delete_after, StringSchema, require_group: :processor
|
24
|
+
|
25
|
+
self.or_schema = ArgumentlessTransformSchema
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class TrueSchema < Schema
|
6
|
+
class << self
|
7
|
+
def validate(node)
|
8
|
+
error(node, 'be true') unless to_ruby(node)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_ruby(node)
|
13
|
+
node.to_ruby == true || node.to_ruby == 'true'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class ValueMatcherSchema < ObjectSchema
|
6
|
+
attribute :arguments, ValueOrArraySchema[ArgumentPositionSchema], aliases: :argument,
|
7
|
+
require_group: :matcher
|
8
|
+
attribute :keywords, ValueOrArraySchema[StringPatternSchema], aliases: :keyword,
|
9
|
+
require_group: :matcher
|
10
|
+
attribute :itself, TrueSchema, require_group: :matcher
|
11
|
+
attribute :nested, ValueOrArraySchema[ValueMatcherSchema]
|
12
|
+
attribute :value, StringSchema, require_group: :matcher
|
13
|
+
attribute :recursive, TrueSchema
|
14
|
+
|
15
|
+
self.or_schema = ScalarArgumentSchema
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class ValueOrArraySchema < 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
|
+
if node.array?
|
22
|
+
validate_length(node) && validate_values(node)
|
23
|
+
else
|
24
|
+
validate_or_schema(node)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_ruby(node)
|
29
|
+
if node.array?
|
30
|
+
node.children.map do |value|
|
31
|
+
value_schema.to_ruby(value)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
value_schema.to_ruby(node)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_or_schema(node)
|
41
|
+
value_schema.validate(node)
|
42
|
+
return true if node.valid?
|
43
|
+
|
44
|
+
node.error += ' or an array'
|
45
|
+
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_length(node)
|
50
|
+
self.class.error(node, 'not be empty') if node.children.empty?
|
51
|
+
|
52
|
+
node.valid?
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_values(node)
|
56
|
+
node.children.each do |value|
|
57
|
+
value_schema.validate(value)
|
58
|
+
end
|
59
|
+
|
60
|
+
node.children.all?(&:valid?)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Leftovers
|
4
|
+
class ConfigLoader
|
5
|
+
class ValueProcessorSchema < ObjectSchema
|
6
|
+
inherit_attributes_from ValueMatcherSchema
|
7
|
+
|
8
|
+
attribute :transforms, ValueOrArraySchema[TransformSchema]
|
9
|
+
inherit_attributes_from TransformSchema, require_group: nil
|
10
|
+
|
11
|
+
self.or_schema = ScalarArgumentSchema
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|