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