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.
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