nxt_schema 0.1.2 → 1.0.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/.ruby-version +1 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +32 -29
- data/README.md +186 -116
- data/lib/nxt_schema.rb +56 -49
- data/lib/nxt_schema/{node.rb → application.rb} +1 -1
- data/lib/nxt_schema/application/any_of.rb +40 -0
- data/lib/nxt_schema/application/base.rb +116 -0
- data/lib/nxt_schema/application/collection.rb +57 -0
- data/lib/nxt_schema/application/error_store.rb +57 -0
- data/lib/nxt_schema/application/errors/schema_error.rb +15 -0
- data/lib/nxt_schema/application/errors/validation_error.rb +15 -0
- data/lib/nxt_schema/application/leaf.rb +15 -0
- data/lib/nxt_schema/application/schema.rb +114 -0
- data/lib/nxt_schema/callable.rb +21 -55
- data/lib/nxt_schema/dsl.rb +41 -31
- data/lib/nxt_schema/error.rb +4 -0
- data/lib/nxt_schema/errors/invalid.rb +16 -0
- data/lib/nxt_schema/errors/{error.rb → invalid_options.rb} +1 -2
- data/lib/nxt_schema/missing_input.rb +9 -0
- data/lib/nxt_schema/node/any_of.rb +51 -0
- data/lib/nxt_schema/node/base.rb +135 -233
- data/lib/nxt_schema/node/collection.rb +10 -65
- data/lib/nxt_schema/node/has_sub_nodes.rb +81 -0
- data/lib/nxt_schema/node/leaf.rb +1 -31
- data/lib/nxt_schema/node/maybe_evaluator.rb +15 -10
- data/lib/nxt_schema/node/on_evaluator.rb +25 -0
- data/lib/nxt_schema/node/schema.rb +8 -134
- data/lib/nxt_schema/node/sub_nodes.rb +22 -0
- data/lib/nxt_schema/node/type_system_resolver.rb +22 -0
- data/lib/nxt_schema/types.rb +1 -1
- data/lib/nxt_schema/validators/attribute.rb +3 -3
- data/lib/nxt_schema/validators/{equality.rb → equal_to.rb} +5 -5
- data/lib/nxt_schema/validators/error_messages.rb +42 -0
- data/lib/nxt_schema/{error_messages → validators/error_messages}/en.yaml +3 -3
- data/lib/nxt_schema/validators/{excluded.rb → excluded_in.rb} +4 -4
- data/lib/nxt_schema/validators/excludes.rb +3 -3
- data/lib/nxt_schema/validators/greater_than.rb +3 -3
- data/lib/nxt_schema/validators/greater_than_or_equal.rb +3 -3
- data/lib/nxt_schema/validators/{included.rb → included_in.rb} +4 -4
- data/lib/nxt_schema/validators/includes.rb +3 -3
- data/lib/nxt_schema/validators/less_than.rb +3 -3
- data/lib/nxt_schema/validators/less_than_or_equal.rb +3 -3
- data/lib/nxt_schema/validators/optional_node.rb +13 -8
- data/lib/nxt_schema/validators/pattern.rb +3 -3
- data/lib/nxt_schema/validators/query.rb +4 -4
- data/lib/nxt_schema/validators/registry.rb +1 -7
- data/lib/nxt_schema/{node → validators}/validate_with_proxy.rb +8 -8
- data/lib/nxt_schema/validators/validator.rb +2 -2
- data/lib/nxt_schema/version.rb +1 -1
- data/nxt_schema.gemspec +1 -0
- metadata +42 -22
- data/lib/nxt_schema/callable_or_value.rb +0 -72
- data/lib/nxt_schema/error_messages.rb +0 -40
- data/lib/nxt_schema/errors.rb +0 -4
- data/lib/nxt_schema/errors/invalid_options_error.rb +0 -5
- data/lib/nxt_schema/errors/schema_not_applied_error.rb +0 -5
- data/lib/nxt_schema/node/constructor.rb +0 -9
- data/lib/nxt_schema/node/default_value_evaluator.rb +0 -20
- data/lib/nxt_schema/node/error.rb +0 -13
- data/lib/nxt_schema/node/has_subnodes.rb +0 -97
- data/lib/nxt_schema/node/template_store.rb +0 -15
- data/lib/nxt_schema/registry.rb +0 -85
- data/lib/nxt_schema/undefined.rb +0 -7
data/lib/nxt_schema.rb
CHANGED
@@ -1,68 +1,75 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'nxt_schema/version'
|
2
|
+
require 'active_support/all'
|
3
3
|
require 'dry-types'
|
4
4
|
require 'nxt_registry'
|
5
|
+
require 'nxt_init'
|
5
6
|
require 'yaml'
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
require_relative 'nxt_schema/types'
|
9
|
+
require_relative 'nxt_schema/callable'
|
10
|
+
require_relative 'nxt_schema/application'
|
11
|
+
require_relative 'nxt_schema/missing_input'
|
12
|
+
require_relative 'nxt_schema/error'
|
13
|
+
require_relative 'nxt_schema/errors/invalid'
|
14
|
+
require_relative 'nxt_schema/errors/invalid_options'
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
require_relative 'nxt_schema/validators/registry'
|
17
|
+
require_relative 'nxt_schema/validators/validate_with_proxy'
|
18
|
+
require_relative 'nxt_schema/validators/error_messages'
|
19
|
+
require_relative 'nxt_schema/validators/validator'
|
20
|
+
require_relative 'nxt_schema/validators/attribute'
|
21
|
+
require_relative 'nxt_schema/validators/equal_to'
|
22
|
+
require_relative 'nxt_schema/validators/optional_node'
|
23
|
+
require_relative 'nxt_schema/validators/greater_than'
|
24
|
+
require_relative 'nxt_schema/validators/greater_than_or_equal'
|
25
|
+
require_relative 'nxt_schema/validators/less_than'
|
26
|
+
require_relative 'nxt_schema/validators/less_than_or_equal'
|
27
|
+
require_relative 'nxt_schema/validators/pattern'
|
28
|
+
require_relative 'nxt_schema/validators/included_in'
|
29
|
+
require_relative 'nxt_schema/validators/includes'
|
30
|
+
require_relative 'nxt_schema/validators/excluded_in'
|
31
|
+
require_relative 'nxt_schema/validators/excludes'
|
32
|
+
require_relative 'nxt_schema/validators/query'
|
19
33
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
require "nxt_schema/validators/includes"
|
32
|
-
require "nxt_schema/validators/excluded"
|
33
|
-
require "nxt_schema/validators/excludes"
|
34
|
-
require "nxt_schema/validators/query"
|
34
|
+
require_relative 'nxt_schema/node/on_evaluator'
|
35
|
+
require_relative 'nxt_schema/node/maybe_evaluator'
|
36
|
+
require_relative 'nxt_schema/node/type_resolver'
|
37
|
+
require_relative 'nxt_schema/node/type_system_resolver'
|
38
|
+
require_relative 'nxt_schema/node/base'
|
39
|
+
require_relative 'nxt_schema/node/sub_nodes'
|
40
|
+
require_relative 'nxt_schema/node/has_sub_nodes'
|
41
|
+
require_relative 'nxt_schema/node/any_of'
|
42
|
+
require_relative 'nxt_schema/node/collection'
|
43
|
+
require_relative 'nxt_schema/node/schema'
|
44
|
+
require_relative 'nxt_schema/node/leaf'
|
35
45
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
require "nxt_schema/node/collection"
|
46
|
-
require "nxt_schema/node/leaf"
|
47
|
-
require "nxt_schema/dsl"
|
46
|
+
require_relative 'nxt_schema/application/errors/schema_error'
|
47
|
+
require_relative 'nxt_schema/application/errors/validation_error'
|
48
|
+
require_relative 'nxt_schema/application/error_store'
|
49
|
+
require_relative 'nxt_schema/application/base'
|
50
|
+
require_relative 'nxt_schema/application/any_of'
|
51
|
+
require_relative 'nxt_schema/application/leaf'
|
52
|
+
require_relative 'nxt_schema/application/collection'
|
53
|
+
require_relative 'nxt_schema/application/schema'
|
54
|
+
require_relative 'nxt_schema/dsl'
|
48
55
|
|
49
56
|
module NxtSchema
|
57
|
+
extend Dsl
|
58
|
+
|
59
|
+
def register_error_messages(*paths)
|
60
|
+
Validators::ErrorMessages.load(paths)
|
61
|
+
end
|
62
|
+
|
50
63
|
def register_validator(validator, *keys)
|
51
|
-
keys.each
|
52
|
-
NxtSchema::Validators::Registry::VALIDATORS.register(key, validator)
|
53
|
-
end
|
64
|
+
keys.each { |key| NxtSchema::Validators::REGISTRY.register(key, validator) }
|
54
65
|
end
|
55
66
|
|
56
67
|
def register_type(key, type)
|
57
68
|
NxtSchema::Types.const_set(key.to_s, type)
|
58
69
|
end
|
59
70
|
|
60
|
-
def register_error_messages(*paths)
|
61
|
-
ErrorMessages.load(paths)
|
62
|
-
end
|
63
|
-
|
64
71
|
# Load default messages
|
65
|
-
ErrorMessages.load
|
72
|
+
Validators::ErrorMessages.load
|
66
73
|
|
67
|
-
module_function :
|
74
|
+
module_function :register_error_messages, :register_validator, :register_type
|
68
75
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Application
|
3
|
+
class AnyOf < Application::Base
|
4
|
+
def valid?
|
5
|
+
valid_application.present?
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
# TODO: We should check that this is not empty!
|
10
|
+
child_applications.map(&:call)
|
11
|
+
|
12
|
+
if valid?
|
13
|
+
self.output = valid_application.output
|
14
|
+
else
|
15
|
+
child_applications.each do |application|
|
16
|
+
merge_errors(application)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
delegate :[], to: :child_applications
|
26
|
+
|
27
|
+
def valid_application
|
28
|
+
child_applications.find(&:valid?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def child_applications
|
32
|
+
@child_applications ||= nodes.map { |node| node.build_application(input: input, context: context, parent: self) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def nodes
|
36
|
+
@nodes ||= node.sub_nodes.values
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Application
|
3
|
+
class Base
|
4
|
+
def initialize(node:, input: MissingInput.new, parent:, context:, error_key:)
|
5
|
+
@node = node
|
6
|
+
@input = input
|
7
|
+
@parent = parent
|
8
|
+
@output = nil
|
9
|
+
@error_key = error_key
|
10
|
+
@context = context || parent&.context
|
11
|
+
@applied = false
|
12
|
+
@applied_nodes = parent&.applied_nodes || []
|
13
|
+
@is_root = parent.nil?
|
14
|
+
@root = parent.nil? ? self : parent.root
|
15
|
+
@errors = ErrorStore.new(self)
|
16
|
+
@locale = node.options.fetch(:locale) { parent&.locale || 'en' }.to_s
|
17
|
+
|
18
|
+
@index = error_key
|
19
|
+
resolve_error_key(error_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :output, :node, :input
|
23
|
+
attr_reader :parent, :context, :error_key, :applied, :applied_nodes, :root, :errors, :locale, :index
|
24
|
+
|
25
|
+
def call
|
26
|
+
raise NotImplementedError, 'Implement this in our sub class'
|
27
|
+
end
|
28
|
+
|
29
|
+
delegate :name, :options, to: :node
|
30
|
+
|
31
|
+
def root?
|
32
|
+
@is_root
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid?
|
36
|
+
errors.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_error(error)
|
40
|
+
errors.add_validation_error(message: error)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_schema_error(error)
|
44
|
+
errors.add_schema_error(message: error)
|
45
|
+
end
|
46
|
+
|
47
|
+
def merge_errors(application)
|
48
|
+
errors.merge_errors(application)
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_validations
|
52
|
+
return false unless applied?
|
53
|
+
|
54
|
+
node.validations.each do |validation|
|
55
|
+
args = [self, input]
|
56
|
+
validation.call(*args.take(validation.arity))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def up(levels = 1)
|
61
|
+
0.upto(levels - 1).inject(self) do |acc, _|
|
62
|
+
parent = acc.send(:parent)
|
63
|
+
break acc unless parent
|
64
|
+
|
65
|
+
parent
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
attr_writer :applied, :root
|
72
|
+
|
73
|
+
def coerce_input
|
74
|
+
output = input.is_a?(MissingInput) && node.omnipresent? ? input : node.type[input]
|
75
|
+
self.output = output
|
76
|
+
|
77
|
+
rescue Dry::Types::CoercionError => error
|
78
|
+
add_schema_error(error.message)
|
79
|
+
end
|
80
|
+
|
81
|
+
def apply_on_evaluators
|
82
|
+
node.on_evaluators.each { |evaluator| evaluator.call(input, self, context) { |result| self.input = result } }
|
83
|
+
end
|
84
|
+
|
85
|
+
def maybe_evaluator_applies?
|
86
|
+
@maybe_evaluator_applies ||= node.maybe_evaluators.inject(false) do |acc, evaluator|
|
87
|
+
result = (acc || evaluator.call(input, self, context))
|
88
|
+
|
89
|
+
if result
|
90
|
+
self.output = input
|
91
|
+
break true
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def register_as_applied_when_valid
|
99
|
+
return unless valid?
|
100
|
+
|
101
|
+
self.applied = true
|
102
|
+
applied_nodes << self
|
103
|
+
end
|
104
|
+
|
105
|
+
def resolve_error_key(key)
|
106
|
+
parts = [parent&.error_key].compact
|
107
|
+
parts << (key.present? ? "#{node.name}[#{key}]" : node.name)
|
108
|
+
@error_key = parts.join('.')
|
109
|
+
end
|
110
|
+
|
111
|
+
def applied?
|
112
|
+
@applied
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Application
|
3
|
+
class Collection < Application::Base
|
4
|
+
def call
|
5
|
+
apply_on_evaluators
|
6
|
+
child_applications # build applications here so we can access them even when invalid
|
7
|
+
return self if maybe_evaluator_applies?
|
8
|
+
|
9
|
+
coerce_input
|
10
|
+
validate_filled
|
11
|
+
return self unless valid?
|
12
|
+
|
13
|
+
child_applications.each_with_index do |item, index|
|
14
|
+
current_application = item.call
|
15
|
+
|
16
|
+
if !current_application.valid?
|
17
|
+
merge_errors(current_application)
|
18
|
+
else
|
19
|
+
output[index] = current_application.output
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
register_as_applied_when_valid
|
24
|
+
run_validations
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
delegate :[], to: :child_applications
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_filled
|
34
|
+
add_schema_error('is not allowed to be empty') if input.blank? && !maybe_evaluator_applies?
|
35
|
+
end
|
36
|
+
|
37
|
+
def child_applications
|
38
|
+
@child_applications ||= begin
|
39
|
+
return [] unless input.respond_to?(:each_with_index)
|
40
|
+
|
41
|
+
input.each_with_index.map do |item, index|
|
42
|
+
build_child_application(item, index)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_child_application(item, error_key)
|
49
|
+
sub_node.build_application(input: item, context: context, parent: self, error_key: error_key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def sub_node
|
53
|
+
@sub_node ||= node.sub_nodes.values.first
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module NxtSchema
|
2
|
+
module Application
|
3
|
+
class ErrorStore < ::Hash
|
4
|
+
def initialize(application)
|
5
|
+
super()
|
6
|
+
@application = application
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :application
|
10
|
+
|
11
|
+
def add_schema_error(message:)
|
12
|
+
add_error(
|
13
|
+
application,
|
14
|
+
NxtSchema::Application::Errors::SchemaError.new(
|
15
|
+
application: application,
|
16
|
+
message: message
|
17
|
+
)
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_validation_error(message:)
|
22
|
+
add_error(
|
23
|
+
application,
|
24
|
+
NxtSchema::Application::Errors::ValidationError.new(
|
25
|
+
application: application,
|
26
|
+
message: message
|
27
|
+
)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def merge_errors(application)
|
32
|
+
merge!(application.errors)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_error(application, error)
|
36
|
+
self[application.error_key] ||= []
|
37
|
+
self[application.error_key] << error
|
38
|
+
end
|
39
|
+
|
40
|
+
# def schema_errors
|
41
|
+
# inject({}) do |acc, (k, v)|
|
42
|
+
# errors = v.select { |e| e.is_a?(NxtSchema::Application::Errors::SchemaError) }
|
43
|
+
# acc[k] = errors if errors.any?
|
44
|
+
# acc
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def validation_errors
|
49
|
+
# inject({}) do |acc, (k, v)|
|
50
|
+
# errors = v.select { |e| e.is_a?(NxtSchema::Application::Errors::ValidationError) }
|
51
|
+
# acc[k] = errors if errors.any?
|
52
|
+
# acc
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|