nxt_schema 0.1.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +40 -42
  5. data/README.md +267 -121
  6. data/lib/nxt_schema.rb +60 -51
  7. data/lib/nxt_schema/callable.rb +21 -55
  8. data/lib/nxt_schema/dsl.rb +41 -31
  9. data/lib/nxt_schema/error.rb +4 -0
  10. data/lib/nxt_schema/errors/{error.rb → coercion_error.rb} +1 -2
  11. data/lib/nxt_schema/errors/invalid.rb +16 -0
  12. data/lib/nxt_schema/errors/invalid_options.rb +6 -0
  13. data/lib/nxt_schema/node/any_of.rb +39 -0
  14. data/lib/nxt_schema/node/base.rb +66 -267
  15. data/lib/nxt_schema/node/collection.rb +40 -56
  16. data/lib/nxt_schema/node/error_store.rb +41 -0
  17. data/lib/nxt_schema/node/errors/schema_error.rb +15 -0
  18. data/lib/nxt_schema/node/errors/validation_error.rb +15 -0
  19. data/lib/nxt_schema/node/leaf.rb +8 -36
  20. data/lib/nxt_schema/node/schema.rb +70 -103
  21. data/lib/nxt_schema/registry.rb +12 -74
  22. data/lib/nxt_schema/registry/proxy.rb +21 -0
  23. data/lib/nxt_schema/template/any_of.rb +50 -0
  24. data/lib/nxt_schema/template/base.rb +220 -0
  25. data/lib/nxt_schema/template/collection.rb +23 -0
  26. data/lib/nxt_schema/template/has_sub_nodes.rb +87 -0
  27. data/lib/nxt_schema/template/leaf.rb +13 -0
  28. data/lib/nxt_schema/template/maybe_evaluator.rb +28 -0
  29. data/lib/nxt_schema/template/on_evaluator.rb +25 -0
  30. data/lib/nxt_schema/template/schema.rb +22 -0
  31. data/lib/nxt_schema/template/sub_nodes.rb +22 -0
  32. data/lib/nxt_schema/template/type_resolver.rb +39 -0
  33. data/lib/nxt_schema/template/type_system_resolver.rb +22 -0
  34. data/lib/nxt_schema/types.rb +7 -4
  35. data/lib/nxt_schema/undefined.rb +4 -2
  36. data/lib/nxt_schema/validators/{equality.rb → equal_to.rb} +2 -2
  37. data/lib/nxt_schema/validators/error_messages.rb +42 -0
  38. data/lib/nxt_schema/{error_messages → validators/error_messages}/en.yaml +6 -5
  39. data/lib/nxt_schema/validators/{excluded.rb → excluded_in.rb} +1 -1
  40. data/lib/nxt_schema/validators/{included.rb → included_in.rb} +1 -1
  41. data/lib/nxt_schema/validators/includes.rb +1 -1
  42. data/lib/nxt_schema/validators/optional_node.rb +11 -6
  43. data/lib/nxt_schema/validators/registry.rb +1 -7
  44. data/lib/nxt_schema/{node → validators}/validate_with_proxy.rb +3 -3
  45. data/lib/nxt_schema/validators/validator.rb +2 -2
  46. data/lib/nxt_schema/version.rb +1 -1
  47. data/nxt_schema.gemspec +1 -0
  48. metadata +44 -21
  49. data/lib/nxt_schema/callable_or_value.rb +0 -72
  50. data/lib/nxt_schema/error_messages.rb +0 -40
  51. data/lib/nxt_schema/errors.rb +0 -4
  52. data/lib/nxt_schema/errors/invalid_options_error.rb +0 -5
  53. data/lib/nxt_schema/errors/schema_not_applied_error.rb +0 -5
  54. data/lib/nxt_schema/node/constructor.rb +0 -9
  55. data/lib/nxt_schema/node/default_value_evaluator.rb +0 -20
  56. data/lib/nxt_schema/node/error.rb +0 -13
  57. data/lib/nxt_schema/node/has_subnodes.rb +0 -97
  58. data/lib/nxt_schema/node/maybe_evaluator.rb +0 -23
  59. data/lib/nxt_schema/node/template_store.rb +0 -15
  60. data/lib/nxt_schema/node/type_resolver.rb +0 -24
@@ -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,23 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class MaybeEvaluator
4
- def initialize(node, evaluator, value)
5
- @node = node
6
- @evaluator = evaluator
7
- @value = value
8
- end
9
-
10
- attr_reader :node, :evaluator, :value
11
-
12
- def call
13
- if evaluator.respond_to?(:call)
14
- Callable.new(evaluator).call(node, value)
15
- elsif value.is_a?(Symbol) && value.respond_to?(evaluator)
16
- Callable.new(evaluator).bind(value).call(node, value)
17
- else
18
- value == evaluator
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -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,24 +0,0 @@
1
- module NxtSchema
2
- module Node
3
- class TypeResolver
4
- def resolve(type_system, type)
5
- @resolve ||= {}
6
- @resolve[type] ||= begin
7
- if type.is_a?(Dry::Types::Type)
8
- type
9
- else
10
- # Try to resolve in type system
11
- type = type_system.const_get(type.to_s.classify)
12
-
13
- if type.is_a?(Dry::Types::Type)
14
- type
15
- else
16
- # in case it does not exist fallback to Types::Nominal
17
- "NxtSchema::Types::Nominal::#{type.to_s.classify}".constantize
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end