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,72 +0,0 @@
1
- module NxtSchema
2
- class CallableOrValue
3
- def initialize(callee)
4
- @callee = callee
5
-
6
- if callee.is_a?(Symbol)
7
- self.type = :method
8
- elsif callee.respond_to?(:call)
9
- self.type = :proc
10
- self.context = callee.binding
11
- else
12
- self.type = :value
13
- end
14
- end
15
-
16
- def bind(execution_context = nil)
17
- self.context = execution_context
18
- ensure_context_not_missing
19
- self
20
- end
21
-
22
- # NOTE: Currently we only allow arguments! Not keyword args or **options
23
- # If we would allow **options and we would pass a hash as the only argument it would
24
- # automatically be parsed as the options!
25
- def call(*args)
26
- return callee if value?
27
-
28
- ensure_context_not_missing
29
-
30
- args = args.take(arity)
31
-
32
- if method?
33
- context.send(callee, *args)
34
- else
35
- context.instance_exec(*args, &callee)
36
- end
37
- end
38
-
39
- def arity
40
- if proc?
41
- callee.arity
42
- elsif method?
43
- method = context.send(:method, callee)
44
- method.arity
45
- else
46
- raise ArgumentError, "Can't resolve arity from #{callee}"
47
- end
48
- end
49
-
50
- private
51
-
52
- def proc?
53
- type == :proc
54
- end
55
-
56
- def method?
57
- type == :method
58
- end
59
-
60
- def value?
61
- type == :value
62
- end
63
-
64
- def ensure_context_not_missing
65
- return if context
66
-
67
- raise ArgumentError, "Missing context: #{context}"
68
- end
69
-
70
- attr_accessor :context, :callee, :type
71
- end
72
- end
@@ -1,40 +0,0 @@
1
- module NxtSchema
2
- class ErrorMessages
3
- class << self
4
- def values
5
- @values ||= {}
6
- end
7
-
8
- def values=(value)
9
- @values = value
10
- end
11
-
12
- def load(paths = files)
13
- Array(paths).each do |path|
14
- new_values = YAML.load(ERB.new(File.read(path)).result).with_indifferent_access
15
- self.values = values.deep_merge!(new_values)
16
- end
17
- end
18
-
19
- def resolve(locale, key, **options)
20
- message = begin
21
- values.fetch(locale).fetch(key)
22
- rescue KeyError
23
- raise "Could not resolve error message for #{locale}->#{key}"
24
- end
25
-
26
- message % options
27
- end
28
-
29
- def files
30
- @files ||= begin
31
- files = Dir.entries(File.expand_path('../error_messages/', __FILE__)).map do |filename|
32
- File.expand_path("../error_messages/#{filename}", __FILE__)
33
- end
34
-
35
- files.select { |f| !File.directory? f }
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,4 +0,0 @@
1
- module NxtSchema
2
- module Errors
3
- end
4
- end
@@ -1,5 +0,0 @@
1
- module NxtSchema
2
- module Errors
3
- InvalidOptionsError = Class.new(Error)
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- module NxtSchema
2
- module Errors
3
- SchemaNotAppliedError = Class.new(Error)
4
- end
5
- end
@@ -1,9 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class Constructor < Node::Schema
4
- def initialize(name:, type: NxtSchema::Types::Constructor(::OpenStruct), parent_node:, **options, &block)
5
- super
6
- end
7
- end
8
- end
9
- end
@@ -1,20 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class DefaultValueEvaluator
4
- def initialize(node, evaluator_or_value)
5
- @node = node
6
- @evaluator_or_value = evaluator_or_value
7
- end
8
-
9
- attr_reader :node, :evaluator_or_value
10
-
11
- def call
12
- if evaluator_or_value.respond_to?(:call)
13
- Callable.new(evaluator_or_value).call(node)
14
- else
15
- evaluator_or_value
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class Error
4
- def initialize(path, value, message)
5
- @path = path
6
- @value = value
7
- @message = message
8
- end
9
-
10
- attr_reader :path, :value, :message
11
- end
12
- end
13
- end
@@ -1,97 +0,0 @@
1
- require_relative 'schema'
2
- require_relative 'collection'
3
- require_relative 'constructor'
4
- require_relative 'leaf'
5
-
6
- module NxtSchema
7
- module Node
8
- module HasSubNodes
9
- attr_accessor :template_store, :value_store
10
-
11
- # TODO: Would be cool if we could register custom node types!
12
- def node(name, type_or_node, **options, &block)
13
- child_node = case type_or_node.to_s.to_sym
14
- when :Schema
15
- NxtSchema::Node::Schema.new(name: name, type: NxtSchema::Types::Strict::Hash, parent_node: self, **options, &block)
16
- when :Collection
17
- NxtSchema::Node::Collection.new(name: name, type: NxtSchema::Types::Strict::Array, parent_node: self, **options, &block)
18
- when :Struct
19
- NxtSchema::Node::Constructor.new(
20
- name: name,
21
- type: NxtSchema::Types::Constructor(::Struct) { |hash| ::Struct.new(*hash.keys).new(*hash.values) },
22
- parent_node: self,
23
- **options,
24
- &block
25
- )
26
- when :OpenStruct
27
- NxtSchema::Node::Constructor.new(
28
- name: name,
29
- type: NxtSchema::Types::Constructor(::OpenStruct),
30
- parent_node: self,
31
- **options,
32
- &block
33
- )
34
- else
35
- if type_or_node.is_a?(NxtSchema::Node::Base)
36
- node = type_or_node.clone
37
- node.options.merge!(options)
38
- node.name = name
39
- node.parent_node = self
40
- node
41
- else
42
- NxtSchema::Node::Leaf.new(name: name, type: type_or_node, parent_node: self, **options)
43
- end
44
- end
45
-
46
- # TODO: Should we check if there is a
47
- raise KeyError, "Duplicate registration for key: #{name}" if template_store.key?(name)
48
- template_store.push(child_node)
49
-
50
- child_node
51
- end
52
-
53
- def required(name, type, **options, &block)
54
- node(name, type, options, &block)
55
- end
56
-
57
- alias_method :requires, :required
58
-
59
- def nodes(name, **options, &block)
60
- node(name, :Collection, options, &block)
61
- end
62
-
63
- alias_method :array, :nodes
64
-
65
- def schema(name, **options, &block)
66
- node(name, :Schema, options, &block)
67
- end
68
-
69
- alias_method :hash, :schema
70
-
71
- def struct(name, **options, &block)
72
- node(name, NxtSchema::Types::Constructor(::OpenStruct), options, &block)
73
- end
74
-
75
- def dup
76
- result = super
77
- result.template_store = template_store.deep_dup
78
- result.options = options.deep_dup
79
- result
80
- end
81
-
82
- delegate_missing_to :value_store
83
-
84
- private
85
-
86
- def value_violates_emptiness?(value)
87
- return true unless value.respond_to?(:empty?)
88
-
89
- value.empty?
90
- end
91
- end
92
- end
93
- end
94
-
95
- NxtSchema::Node::Schema.include(::NxtSchema::Node::HasSubNodes)
96
- NxtSchema::Node::Collection.include(::NxtSchema::Node::HasSubNodes)
97
- NxtSchema::Node::Constructor.include(::NxtSchema::Node::HasSubNodes)
@@ -1,15 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class TemplateStore < ::Hash
4
- def push(node)
5
- node_name = node.name
6
- raise_key_error(node_name) if key?(node_name)
7
- self[node_name] = node
8
- end
9
-
10
- def raise_key_error(key)
11
- raise KeyError, "Node with name '#{key}' already registered! Node names must be unique!"
12
- end
13
- end
14
- end
15
- end
@@ -1,85 +0,0 @@
1
- module NxtSchema
2
- class Registry
3
- def initialize(namespace_separator: '::', namespace: '')
4
- @store = ActiveSupport::HashWithIndifferentAccess.new
5
- @namespace_separator = namespace_separator
6
- @namespace = namespace
7
- end
8
-
9
- delegate_missing_to :store
10
-
11
- # register('strict::string')
12
- # Registry[:strict].register
13
-
14
- def register(key, value)
15
- key = key.to_s
16
- ensure_key_not_registered_already(key)
17
- namespaced_store(key)[flat_key(key)] = value
18
- end
19
-
20
- def resolve(key, *args)
21
- value = resolve_value(key)
22
- return value unless value.respond_to?(:call)
23
-
24
- value.call(*args)
25
- end
26
-
27
- def resolve_value(key)
28
- key = key.to_s
29
- parts = namespaced_key_parts(key)[0..-2]
30
-
31
- namespaced_store = parts.inject(store) do |acc, key|
32
- acc.fetch(key)
33
- rescue KeyError
34
- raise KeyError, "No registry found at #{key} in #{acc}"
35
- end
36
-
37
- begin
38
- namespaced_store.fetch(flat_key(key))
39
- rescue KeyError
40
- raise KeyError, "Could not find #{flat_key(key)} in #{namespaced_store}"
41
- end
42
- end
43
-
44
- private
45
-
46
- attr_reader :store, :namespace_separator, :namespace
47
-
48
- def namespaced_store(key)
49
- parts = namespaced_key_parts(key)
50
-
51
- current_parts = []
52
-
53
- parts[0..-2].inject(store) do |acc, namespace|
54
- current_parts << namespace
55
- current_namespace = current_parts.join(namespace_separator)
56
-
57
- acc.fetch(namespace) do
58
- acc[namespace] = Registry.new(namespace: current_namespace)
59
- acc = acc[namespace]
60
- acc
61
- end
62
- end
63
- end
64
-
65
- def namespaced_key_parts(key)
66
- key.downcase.split(namespace_separator)
67
- end
68
-
69
- def flat_key(key)
70
- namespaced_key_parts(key).last
71
- end
72
-
73
- def ensure_key_not_registered_already(key)
74
- return unless namespaced_store(key).key?(flat_key(key))
75
-
76
- raise KeyError, "Key: #{flat_key(key)} already registered in #{namespaced_store(key)}"
77
- end
78
-
79
- def to_s
80
- identifier = 'NxtSchema::Registry'
81
- identifier << "#{namespace_separator}#{namespace}" unless namespace.blank?
82
- identifier
83
- end
84
- end
85
- end
@@ -1,7 +0,0 @@
1
- module NxtSchema
2
- class Undefined
3
- def inspect
4
- 'undefined'
5
- end
6
- end
7
- end