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