nxt_schema 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -3
  3. data/README.md +80 -38
  4. data/lib/nxt_schema.rb +17 -17
  5. data/lib/nxt_schema/dsl.rb +7 -7
  6. data/lib/nxt_schema/{application.rb → node.rb} +1 -1
  7. data/lib/nxt_schema/node/any_of.rb +21 -33
  8. data/lib/nxt_schema/node/base.rb +70 -171
  9. data/lib/nxt_schema/node/collection.rb +43 -8
  10. data/lib/nxt_schema/{application → node}/error_store.rb +5 -5
  11. data/lib/nxt_schema/{application → node}/errors/schema_error.rb +1 -1
  12. data/lib/nxt_schema/{application → node}/errors/validation_error.rb +1 -1
  13. data/lib/nxt_schema/node/leaf.rb +7 -5
  14. data/lib/nxt_schema/node/schema.rb +101 -8
  15. data/lib/nxt_schema/template/any_of.rb +50 -0
  16. data/lib/nxt_schema/template/base.rb +218 -0
  17. data/lib/nxt_schema/template/collection.rb +23 -0
  18. data/lib/nxt_schema/{node → template}/has_sub_nodes.rb +16 -10
  19. data/lib/nxt_schema/template/leaf.rb +13 -0
  20. data/lib/nxt_schema/{node → template}/maybe_evaluator.rb +1 -1
  21. data/lib/nxt_schema/{node → template}/on_evaluator.rb +1 -1
  22. data/lib/nxt_schema/template/schema.rb +22 -0
  23. data/lib/nxt_schema/{node → template}/sub_nodes.rb +1 -1
  24. data/lib/nxt_schema/{node → template}/type_resolver.rb +2 -2
  25. data/lib/nxt_schema/{node → template}/type_system_resolver.rb +1 -1
  26. data/lib/nxt_schema/version.rb +1 -1
  27. metadata +17 -17
  28. data/lib/nxt_schema/application/any_of.rb +0 -40
  29. data/lib/nxt_schema/application/base.rb +0 -116
  30. data/lib/nxt_schema/application/collection.rb +0 -57
  31. data/lib/nxt_schema/application/leaf.rb +0 -15
  32. data/lib/nxt_schema/application/schema.rb +0 -114
@@ -1,3 +1,3 @@
1
1
  module NxtSchema
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-23 00:00:00.000000000 Z
11
+ date: 2020-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -155,32 +155,32 @@ files:
155
155
  - bin/console
156
156
  - bin/setup
157
157
  - lib/nxt_schema.rb
158
- - lib/nxt_schema/application.rb
159
- - lib/nxt_schema/application/any_of.rb
160
- - lib/nxt_schema/application/base.rb
161
- - lib/nxt_schema/application/collection.rb
162
- - lib/nxt_schema/application/error_store.rb
163
- - lib/nxt_schema/application/errors/schema_error.rb
164
- - lib/nxt_schema/application/errors/validation_error.rb
165
- - lib/nxt_schema/application/leaf.rb
166
- - lib/nxt_schema/application/schema.rb
167
158
  - lib/nxt_schema/callable.rb
168
159
  - lib/nxt_schema/dsl.rb
169
160
  - lib/nxt_schema/error.rb
170
161
  - lib/nxt_schema/errors/invalid.rb
171
162
  - lib/nxt_schema/errors/invalid_options.rb
172
163
  - lib/nxt_schema/missing_input.rb
164
+ - lib/nxt_schema/node.rb
173
165
  - lib/nxt_schema/node/any_of.rb
174
166
  - lib/nxt_schema/node/base.rb
175
167
  - lib/nxt_schema/node/collection.rb
176
- - lib/nxt_schema/node/has_sub_nodes.rb
168
+ - lib/nxt_schema/node/error_store.rb
169
+ - lib/nxt_schema/node/errors/schema_error.rb
170
+ - lib/nxt_schema/node/errors/validation_error.rb
177
171
  - lib/nxt_schema/node/leaf.rb
178
- - lib/nxt_schema/node/maybe_evaluator.rb
179
- - lib/nxt_schema/node/on_evaluator.rb
180
172
  - lib/nxt_schema/node/schema.rb
181
- - lib/nxt_schema/node/sub_nodes.rb
182
- - lib/nxt_schema/node/type_resolver.rb
183
- - lib/nxt_schema/node/type_system_resolver.rb
173
+ - lib/nxt_schema/template/any_of.rb
174
+ - lib/nxt_schema/template/base.rb
175
+ - lib/nxt_schema/template/collection.rb
176
+ - lib/nxt_schema/template/has_sub_nodes.rb
177
+ - lib/nxt_schema/template/leaf.rb
178
+ - lib/nxt_schema/template/maybe_evaluator.rb
179
+ - lib/nxt_schema/template/on_evaluator.rb
180
+ - lib/nxt_schema/template/schema.rb
181
+ - lib/nxt_schema/template/sub_nodes.rb
182
+ - lib/nxt_schema/template/type_resolver.rb
183
+ - lib/nxt_schema/template/type_system_resolver.rb
184
184
  - lib/nxt_schema/types.rb
185
185
  - lib/nxt_schema/validators/attribute.rb
186
186
  - lib/nxt_schema/validators/equal_to.rb
@@ -1,40 +0,0 @@
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
@@ -1,116 +0,0 @@
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
@@ -1,57 +0,0 @@
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
@@ -1,15 +0,0 @@
1
- module NxtSchema
2
- module Application
3
- class Leaf < Application::Base
4
- def call
5
- apply_on_evaluators
6
- return self if maybe_evaluator_applies?
7
-
8
- coerce_input
9
- register_as_applied_when_valid
10
- run_validations
11
- self
12
- end
13
- end
14
- end
15
- end
@@ -1,114 +0,0 @@
1
- module NxtSchema
2
- module Application
3
- class Schema < 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
- return self unless valid?
11
-
12
- flag_missing_keys
13
- apply_additional_keys_strategy
14
-
15
- child_applications.each do |key, child|
16
- current_application = child.call
17
-
18
- if !current_application.valid?
19
- merge_errors(current_application)
20
- else
21
- output[key] = current_application.output
22
- end
23
- end
24
-
25
- transform_keys
26
- register_as_applied_when_valid
27
- run_validations
28
- self
29
- end
30
-
31
- delegate :[], to: :child_applications
32
-
33
- private
34
-
35
- def transform_keys
36
- transformer = node.key_transformer
37
- return unless transformer && output.respond_to?(:transform_keys!)
38
-
39
- output.transform_keys!(&transformer)
40
- end
41
-
42
- def keys
43
- @keys ||= node.sub_nodes.reject { |key, _| optional_and_not_given_key?(key) }.keys
44
- end
45
-
46
- def additional_keys
47
- @additional_keys ||= input.keys - keys
48
- end
49
-
50
- def optional_and_not_given_key?(key)
51
- node.sub_nodes[key].optional? && !input.key?(key)
52
- end
53
-
54
- def additional_keys?
55
- additional_keys.any?
56
- end
57
-
58
- def missing_keys
59
- @missing_keys ||= node.sub_nodes.reject { |_, node| node.omnipresent? || node.optional? }.keys - input.keys
60
- end
61
-
62
- def apply_additional_keys_strategy
63
- return if allow_additional_keys?
64
- return unless additional_keys?
65
-
66
- if restrict_additional_keys?
67
- add_schema_error("Additional keys are not allowed: #{additional_keys}")
68
- elsif reject_additional_keys?
69
- self.output = output.except(*additional_keys)
70
- end
71
- end
72
-
73
- def flag_missing_keys
74
- return if missing_keys.empty?
75
-
76
- add_schema_error("The following keys are missing: #{missing_keys}")
77
- end
78
-
79
- def allow_additional_keys?
80
- node.additional_keys_strategy == :allow
81
- end
82
-
83
- def reject_additional_keys?
84
- node.additional_keys_strategy == :reject
85
- end
86
-
87
- def restrict_additional_keys?
88
- node.additional_keys_strategy == :restrict
89
- end
90
-
91
- def child_applications
92
- @child_applications ||= begin
93
- keys.inject({}) do |acc, key|
94
- child_application = build_child_application(key)
95
- acc[key] = child_application if child_application.present?
96
- acc
97
- end
98
- end
99
- end
100
-
101
- def build_child_application(key)
102
- sub_node = node.sub_nodes[key]
103
- return unless sub_node.present?
104
-
105
- value = input_has_key?(input, key) ? input[key] : MissingInput.new
106
- sub_node.build_application(input: value, context: context, parent: self)
107
- end
108
-
109
- def input_has_key?(input, key)
110
- input.respond_to?(:key?) && input.key?(key)
111
- end
112
- end
113
- end
114
- end