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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +32 -29
  5. data/README.md +186 -116
  6. data/lib/nxt_schema.rb +56 -49
  7. data/lib/nxt_schema/{node.rb → application.rb} +1 -1
  8. data/lib/nxt_schema/application/any_of.rb +40 -0
  9. data/lib/nxt_schema/application/base.rb +116 -0
  10. data/lib/nxt_schema/application/collection.rb +57 -0
  11. data/lib/nxt_schema/application/error_store.rb +57 -0
  12. data/lib/nxt_schema/application/errors/schema_error.rb +15 -0
  13. data/lib/nxt_schema/application/errors/validation_error.rb +15 -0
  14. data/lib/nxt_schema/application/leaf.rb +15 -0
  15. data/lib/nxt_schema/application/schema.rb +114 -0
  16. data/lib/nxt_schema/callable.rb +21 -55
  17. data/lib/nxt_schema/dsl.rb +41 -31
  18. data/lib/nxt_schema/error.rb +4 -0
  19. data/lib/nxt_schema/errors/invalid.rb +16 -0
  20. data/lib/nxt_schema/errors/{error.rb → invalid_options.rb} +1 -2
  21. data/lib/nxt_schema/missing_input.rb +9 -0
  22. data/lib/nxt_schema/node/any_of.rb +51 -0
  23. data/lib/nxt_schema/node/base.rb +135 -233
  24. data/lib/nxt_schema/node/collection.rb +10 -65
  25. data/lib/nxt_schema/node/has_sub_nodes.rb +81 -0
  26. data/lib/nxt_schema/node/leaf.rb +1 -31
  27. data/lib/nxt_schema/node/maybe_evaluator.rb +15 -10
  28. data/lib/nxt_schema/node/on_evaluator.rb +25 -0
  29. data/lib/nxt_schema/node/schema.rb +8 -134
  30. data/lib/nxt_schema/node/sub_nodes.rb +22 -0
  31. data/lib/nxt_schema/node/type_system_resolver.rb +22 -0
  32. data/lib/nxt_schema/types.rb +1 -1
  33. data/lib/nxt_schema/validators/attribute.rb +3 -3
  34. data/lib/nxt_schema/validators/{equality.rb → equal_to.rb} +5 -5
  35. data/lib/nxt_schema/validators/error_messages.rb +42 -0
  36. data/lib/nxt_schema/{error_messages → validators/error_messages}/en.yaml +3 -3
  37. data/lib/nxt_schema/validators/{excluded.rb → excluded_in.rb} +4 -4
  38. data/lib/nxt_schema/validators/excludes.rb +3 -3
  39. data/lib/nxt_schema/validators/greater_than.rb +3 -3
  40. data/lib/nxt_schema/validators/greater_than_or_equal.rb +3 -3
  41. data/lib/nxt_schema/validators/{included.rb → included_in.rb} +4 -4
  42. data/lib/nxt_schema/validators/includes.rb +3 -3
  43. data/lib/nxt_schema/validators/less_than.rb +3 -3
  44. data/lib/nxt_schema/validators/less_than_or_equal.rb +3 -3
  45. data/lib/nxt_schema/validators/optional_node.rb +13 -8
  46. data/lib/nxt_schema/validators/pattern.rb +3 -3
  47. data/lib/nxt_schema/validators/query.rb +4 -4
  48. data/lib/nxt_schema/validators/registry.rb +1 -7
  49. data/lib/nxt_schema/{node → validators}/validate_with_proxy.rb +8 -8
  50. data/lib/nxt_schema/validators/validator.rb +2 -2
  51. data/lib/nxt_schema/version.rb +1 -1
  52. data/nxt_schema.gemspec +1 -0
  53. metadata +42 -22
  54. data/lib/nxt_schema/callable_or_value.rb +0 -72
  55. data/lib/nxt_schema/error_messages.rb +0 -40
  56. data/lib/nxt_schema/errors.rb +0 -4
  57. data/lib/nxt_schema/errors/invalid_options_error.rb +0 -5
  58. data/lib/nxt_schema/errors/schema_not_applied_error.rb +0 -5
  59. data/lib/nxt_schema/node/constructor.rb +0 -9
  60. data/lib/nxt_schema/node/default_value_evaluator.rb +0 -20
  61. data/lib/nxt_schema/node/error.rb +0 -13
  62. data/lib/nxt_schema/node/has_subnodes.rb +0 -97
  63. data/lib/nxt_schema/node/template_store.rb +0 -15
  64. data/lib/nxt_schema/registry.rb +0 -85
  65. data/lib/nxt_schema/undefined.rb +0 -7
@@ -1,68 +1,75 @@
1
- require "nxt_schema/version"
2
- require "active_support/all"
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
- require "nxt_schema/types"
8
- require "nxt_schema/undefined"
9
- require "nxt_schema/registry"
10
- require "nxt_schema/callable"
11
- require "nxt_schema/callable_or_value"
12
- require "nxt_schema/validators/registry"
13
- require "nxt_schema/node/validate_with_proxy"
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
- require "nxt_schema/errors"
16
- require "nxt_schema/errors/error"
17
- require "nxt_schema/errors/schema_not_applied_error"
18
- require "nxt_schema/errors/invalid_options_error"
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
- require "nxt_schema/error_messages"
21
- require "nxt_schema/validators/validator"
22
- require "nxt_schema/validators/attribute"
23
- require "nxt_schema/validators/equality"
24
- require "nxt_schema/validators/optional_node"
25
- require "nxt_schema/validators/greater_than"
26
- require "nxt_schema/validators/greater_than_or_equal"
27
- require "nxt_schema/validators/less_than"
28
- require "nxt_schema/validators/less_than_or_equal"
29
- require "nxt_schema/validators/pattern"
30
- require "nxt_schema/validators/included"
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
- require "nxt_schema/node"
37
- require "nxt_schema/node/type_resolver"
38
- require "nxt_schema/node/maybe_evaluator"
39
- require "nxt_schema/node/default_value_evaluator"
40
- require "nxt_schema/node/base"
41
- require "nxt_schema/node/error"
42
- require "nxt_schema/node/has_subnodes"
43
- require "nxt_schema/node/template_store"
44
- require "nxt_schema/node/schema"
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 do |key|
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 :register_validator, :register_type, :register_error_messages
74
+ module_function :register_error_messages, :register_validator, :register_type
68
75
  end
@@ -1,5 +1,5 @@
1
1
  module NxtSchema
2
- module Node
2
+ module Application
3
3
 
4
4
  end
5
5
  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
@@ -0,0 +1,15 @@
1
+ module NxtSchema
2
+ module Application
3
+ module Errors
4
+ class SchemaError < ::String
5
+ def initialize(application:, message:)
6
+ super(message)
7
+ @application = application
8
+ end
9
+
10
+ attr_reader :application
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ module NxtSchema
2
+ module Application
3
+ module Errors
4
+ class ValidationError < ::String
5
+ def initialize(application:, message:)
6
+ super(message)
7
+ @application = application
8
+ end
9
+
10
+ attr_reader :application
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
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