nxt_schema 0.1.0 → 1.0.2

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 (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
@@ -0,0 +1,13 @@
1
+ module NxtSchema
2
+ module Template
3
+ class Leaf < Template::Base
4
+ def initialize(name:, type: :String, parent_node:, **options, &block)
5
+ super
6
+ end
7
+
8
+ def leaf?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module NxtSchema
2
+ module Template
3
+ class MaybeEvaluator
4
+ def initialize(value:)
5
+ @value = value
6
+ end
7
+
8
+ def call(target = nil, *args)
9
+ evaluator = evaluator(target, *args)
10
+
11
+ if evaluator.value?
12
+ # When a value was given we check if this equals to the input
13
+ evaluator.call == target
14
+ else
15
+ evaluator.call
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def evaluator(target, *args)
22
+ Callable.new(value, target, *args)
23
+ end
24
+
25
+ attr_reader :value
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module NxtSchema
2
+ module Template
3
+ class OnEvaluator
4
+ def initialize(condition:, value:)
5
+ @condition = condition
6
+ @value = value
7
+ end
8
+
9
+ def call(target = nil, *args, &block)
10
+ return unless condition_applies?(target, *args)
11
+
12
+ result = Callable.new(value, target, *args).call
13
+ block.yield(result)
14
+ end
15
+
16
+ private
17
+
18
+ def condition_applies?(target, *args)
19
+ Callable.new(condition, target, *args).call
20
+ end
21
+
22
+ attr_reader :condition, :value
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ module NxtSchema
2
+ module Template
3
+ class Schema < Template::Base
4
+ include HasSubNodes
5
+
6
+ DEFAULT_TYPE = NxtSchema::Types::Strict::Hash
7
+
8
+ def initialize(name:, type: DEFAULT_TYPE, parent_node:, **options, &block)
9
+ super
10
+ ensure_sub_nodes_present
11
+ end
12
+
13
+ def optional(name, node_or_type_of_node, **options, &block)
14
+ node(name, node_or_type_of_node, **options.merge(optional: true), &block)
15
+ end
16
+
17
+ def omnipresent(name, node_or_type_of_node, **options, &block)
18
+ node(name, node_or_type_of_node, **options.merge(omnipresent: true), &block)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module NxtSchema
2
+ module Template
3
+ class SubNodes < ::Hash
4
+ def initialize
5
+ super
6
+ transform_keys { |k| k.to_sym }
7
+ end
8
+
9
+ def add(node)
10
+ node_name = node.name
11
+ ensure_node_name_free(node_name)
12
+ self[node_name] = node
13
+ end
14
+
15
+ def ensure_node_name_free(name)
16
+ return unless key?(name)
17
+
18
+ raise KeyError, "Node with name '#{name}' already exists! Node names must be unique!"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ module NxtSchema
2
+ module Template
3
+ class TypeResolver
4
+ def resolve(type_system, type)
5
+ @resolve ||= {}
6
+ @resolve[type] ||= begin
7
+ if type.is_a?(Symbol)
8
+ resolve_type_from_symbol(type, type_system)
9
+ elsif type.respond_to?(:call)
10
+ type
11
+ else
12
+ raise_type_not_resolvable_error(type)
13
+ end
14
+ rescue NxtRegistry::Errors::KeyNotRegisteredError => error
15
+ raise_type_not_resolvable_error(type)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def resolve_type_from_symbol(type, type_system)
22
+ classified_type = type.to_s.classify
23
+
24
+ return type_system.const_get(classified_type) if type_defined_in_type_system?(type, type_system)
25
+ return NxtSchema::Types::Nominal.const_get(classified_type) if type_defined_in_type_system?(type, NxtSchema::Types::Nominal)
26
+
27
+ NxtSchema::Types.registry(:types).resolve!(type)
28
+ end
29
+
30
+ def type_defined_in_type_system?(type, type_system)
31
+ type_system.constants.include?(type)
32
+ end
33
+
34
+ def raise_type_not_resolvable_error(type)
35
+ raise ArgumentError, "Can't resolve type: #{type}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ module NxtSchema
2
+ module Template
3
+ class TypeSystemResolver
4
+ include NxtInit
5
+ attr_init :node
6
+
7
+ delegate_missing_to :node
8
+
9
+ def call
10
+ type_system = options.fetch(:type_system) { parent_node&.type_system }
11
+
12
+ if type_system.is_a?(Module)
13
+ type_system
14
+ elsif type_system.is_a?(Symbol) || type_system.is_a?(String)
15
+ "NxtSchema::Types::#{type_system.to_s.classify}".constantize
16
+ else
17
+ NxtSchema::Types
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,10 +1,13 @@
1
1
  module NxtSchema
2
2
  module Types
3
3
  include Dry.Types()
4
+ extend NxtRegistry
4
5
 
5
- StrippedString = Strict::String.constructor(->(string) { string&.strip })
6
- StrippedNonBlankString = StrippedString.constrained(min_size: 1)
7
- Enums = -> (*values) { Strict::String.enum(*values) } # Use as NxtSchema::Types::Enums[*ROLES]
8
- SymbolizedEnums = -> (*values) { Coercible::Symbol.enum(*values) } # Use as NxtSchema::Types::SymboleEnums[*ROLES]
6
+ registry(:types, call: false) do
7
+ register(:StrippedString, Strict::String.constructor(->(string) { string&.strip }))
8
+ register(:LengthyStrippedString, resolve!(:StrippedString).constrained(min_size: 1))
9
+ register(:Enum, -> (*values) { Strict::String.enum(*values) })
10
+ register(:SymbolizedEnum, -> (*values) { Coercible::Symbol.enum(*values) })
11
+ end
9
12
  end
10
13
  end
@@ -1,7 +1,9 @@
1
1
  module NxtSchema
2
2
  class Undefined
3
3
  def inspect
4
- 'undefined'
4
+ self.class.name
5
5
  end
6
+
7
+ alias to_s inspect
6
8
  end
7
- end
9
+ end
@@ -5,7 +5,7 @@ module NxtSchema
5
5
  @expectation = expectation
6
6
  end
7
7
 
8
- register_as :equality, :eql
8
+ register_as :equal_to, :eql
9
9
  attr_reader :expectation
10
10
 
11
11
  # Query for equality validator(:equality, 3)
@@ -13,7 +13,7 @@ module NxtSchema
13
13
 
14
14
  def build
15
15
  lambda do |node, value|
16
- expected_value = expectation.respond_to?(:call) ? Callable.new(expectation).call(node, value) : expectation
16
+ expected_value = Callable.new(expectation, nil, value).call
17
17
 
18
18
  if value == expected_value
19
19
  true
@@ -0,0 +1,42 @@
1
+ module NxtSchema
2
+ module Validators
3
+ class ErrorMessages
4
+ class << self
5
+ def values
6
+ @values ||= {}
7
+ end
8
+
9
+ def values=(value)
10
+ @values = value
11
+ end
12
+
13
+ def load(paths = files)
14
+ Array(paths).each do |path|
15
+ new_values = YAML.load(ERB.new(File.read(path)).result).with_indifferent_access
16
+ self.values = values.deep_merge!(new_values)
17
+ end
18
+ end
19
+
20
+ def resolve(locale, key, **options)
21
+ message = begin
22
+ values.fetch(locale).fetch(key)
23
+ rescue KeyError
24
+ raise "Could not resolve error message for #{locale}->#{key}"
25
+ end
26
+
27
+ message % options
28
+ end
29
+
30
+ def files
31
+ @files ||= begin
32
+ files = Dir.entries(File.expand_path('../error_messages/', __FILE__)).map do |filename|
33
+ File.expand_path("../error_messages/#{filename}", __FILE__)
34
+ end
35
+
36
+ files.select { |f| !File.directory? f }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,15 +1,16 @@
1
1
  en:
2
- required_key_missing: "Required key :%{key} is missing in %{target}"
3
- additional_keys_detected: "Additional keys %{keys} not allowed in %{target}"
2
+ required_key_missing: "Required key :%{key} is missing"
3
+ additional_keys_detected: "Additional keys %{keys} not allowed"
4
4
  attribute: "%{attribute} has invalid %{attribute_name} attribute of %{value}"
5
- equality: "%{actual} does not equal %{expected}"
5
+ equal_to: "%{actual} does not equal %{expected}"
6
6
  excludes: "%{value} cannot contain %{target}"
7
7
  includes: "%{value} must include %{target}"
8
- excluded: "%{value} must be excluded in %{target}"
9
- included: "%{value} must be included in %{target}"
8
+ excluded_in: "%{value} must be excluded in %{target}"
9
+ included_in: "%{value} must be included in %{target}"
10
10
  greater_than: "%{value} must be greater than %{threshold}"
11
11
  greater_than_or_equal: "%{value} must be greater than or equal to %{threshold}"
12
12
  less_than: "%{value} must be less than %{threshold}"
13
13
  less_than_or_equal: "%{value} must be less than or equal to %{threshold}"
14
14
  pattern: "%{value} must match pattern %{pattern}"
15
15
  query: "%{value}.%{query} was %{actual} and must be true"
16
+ emptiness: "%{value} cannot not be empty"
@@ -5,7 +5,7 @@ module NxtSchema
5
5
  @target = target
6
6
  end
7
7
 
8
- register_as :excluded
8
+ register_as :excluded_in
9
9
  attr_reader :target
10
10
 
11
11
  def build
@@ -5,7 +5,7 @@ module NxtSchema
5
5
  @target = target
6
6
  end
7
7
 
8
- register_as :included
8
+ register_as :included_in
9
9
  attr_reader :target
10
10
 
11
11
  def build
@@ -13,7 +13,7 @@ module NxtSchema
13
13
  if value.include?(target)
14
14
  true
15
15
  else
16
- message = translate_error(node.locale, value: value, target: target)
16
+ message = translate_error(coerced?.locale, value: value, target: target)
17
17
  node.add_error(message)
18
18
  end
19
19
  end
@@ -13,12 +13,17 @@ module NxtSchema
13
13
  lambda do |node, value|
14
14
  args = [node, value]
15
15
 
16
- if conditional.call(*args.take(conditional.arity))
17
- true
18
- else
19
- message = ErrorMessages.resolve(node.locale, :required_key_missing, key: missing_key, target: node.value)
20
- node.add_error(message)
21
- end
16
+ return if conditional.call(*args.take(conditional.arity))
17
+ return if node.send(:keys).include?(missing_key.to_sym)
18
+
19
+ message = ErrorMessages.resolve(
20
+ node.locale,
21
+ :required_key_missing,
22
+ key: missing_key,
23
+ target: node.input
24
+ )
25
+
26
+ node.add_error(message)
22
27
  end
23
28
  end
24
29
  end
@@ -1,11 +1,5 @@
1
1
  module NxtSchema
2
2
  module Validators
3
- class Registry
4
- extend NxtRegistry
5
-
6
- VALIDATORS = registry :validators, call: false do
7
-
8
- end
9
- end
3
+ REGISTRY = NxtRegistry::Registry.new(call: false)
10
4
  end
11
5
  end
@@ -1,5 +1,5 @@
1
1
  module NxtSchema
2
- module Node
2
+ module Validator
3
3
  class ValidateWithProxy
4
4
  def initialize(node)
5
5
  @node = node
@@ -33,8 +33,8 @@ module NxtSchema
33
33
  attr_reader :aggregated_errors
34
34
 
35
35
  def validator(key, *args)
36
- validator = node.validator(key, *args)
37
- validator.call(self, node.value)
36
+ validator = node.node.send(:validator, key, *args)
37
+ validator.call(self, node.input)
38
38
  end
39
39
  end
40
40
  end
@@ -3,14 +3,14 @@ module NxtSchema
3
3
  class Validator
4
4
  def self.register_as(*keys)
5
5
  keys.each do |key|
6
- NxtSchema::Validators::Registry::VALIDATORS.register(key, self)
6
+ NxtSchema::Validators::REGISTRY.register(key, self)
7
7
  end
8
8
 
9
9
  define_method('key') { @key ||= keys.first }
10
10
  end
11
11
 
12
12
  def translate_error(locale, **options)
13
- NxtSchema::ErrorMessages.resolve(locale, key, **options)
13
+ NxtSchema::Validators::ErrorMessages.resolve(locale, key, **options)
14
14
  end
15
15
  end
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module NxtSchema
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.2"
3
3
  end
data/nxt_schema.gemspec CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "activesupport"
39
39
  spec.add_dependency "dry-types"
40
40
  spec.add_dependency "nxt_registry"
41
+ spec.add_dependency "nxt_init"
41
42
  spec.add_development_dependency "bundler", "~> 1.17"
42
43
  spec.add_development_dependency "rake", "~> 12.3.3"
43
44
  spec.add_development_dependency "rspec", "~> 3.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-20 00:00:00.000000000 Z
11
+ date: 2021-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nxt_init
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -131,6 +145,7 @@ extra_rdoc_files: []
131
145
  files:
132
146
  - ".gitignore"
133
147
  - ".rspec"
148
+ - ".ruby-version"
134
149
  - ".travis.yml"
135
150
  - Gemfile
136
151
  - Gemfile.lock
@@ -141,37 +156,44 @@ files:
141
156
  - bin/setup
142
157
  - lib/nxt_schema.rb
143
158
  - lib/nxt_schema/callable.rb
144
- - lib/nxt_schema/callable_or_value.rb
145
159
  - lib/nxt_schema/dsl.rb
146
- - lib/nxt_schema/error_messages.rb
147
- - lib/nxt_schema/error_messages/en.yaml
148
- - lib/nxt_schema/errors.rb
149
- - lib/nxt_schema/errors/error.rb
150
- - lib/nxt_schema/errors/invalid_options_error.rb
151
- - lib/nxt_schema/errors/schema_not_applied_error.rb
160
+ - lib/nxt_schema/error.rb
161
+ - lib/nxt_schema/errors/coercion_error.rb
162
+ - lib/nxt_schema/errors/invalid.rb
163
+ - lib/nxt_schema/errors/invalid_options.rb
152
164
  - lib/nxt_schema/node.rb
165
+ - lib/nxt_schema/node/any_of.rb
153
166
  - lib/nxt_schema/node/base.rb
154
167
  - lib/nxt_schema/node/collection.rb
155
- - lib/nxt_schema/node/constructor.rb
156
- - lib/nxt_schema/node/default_value_evaluator.rb
157
- - lib/nxt_schema/node/error.rb
158
- - lib/nxt_schema/node/has_subnodes.rb
168
+ - lib/nxt_schema/node/error_store.rb
169
+ - lib/nxt_schema/node/errors/schema_error.rb
170
+ - lib/nxt_schema/node/errors/validation_error.rb
159
171
  - lib/nxt_schema/node/leaf.rb
160
- - lib/nxt_schema/node/maybe_evaluator.rb
161
172
  - lib/nxt_schema/node/schema.rb
162
- - lib/nxt_schema/node/template_store.rb
163
- - lib/nxt_schema/node/type_resolver.rb
164
- - lib/nxt_schema/node/validate_with_proxy.rb
165
173
  - lib/nxt_schema/registry.rb
174
+ - lib/nxt_schema/registry/proxy.rb
175
+ - lib/nxt_schema/template/any_of.rb
176
+ - lib/nxt_schema/template/base.rb
177
+ - lib/nxt_schema/template/collection.rb
178
+ - lib/nxt_schema/template/has_sub_nodes.rb
179
+ - lib/nxt_schema/template/leaf.rb
180
+ - lib/nxt_schema/template/maybe_evaluator.rb
181
+ - lib/nxt_schema/template/on_evaluator.rb
182
+ - lib/nxt_schema/template/schema.rb
183
+ - lib/nxt_schema/template/sub_nodes.rb
184
+ - lib/nxt_schema/template/type_resolver.rb
185
+ - lib/nxt_schema/template/type_system_resolver.rb
166
186
  - lib/nxt_schema/types.rb
167
187
  - lib/nxt_schema/undefined.rb
168
188
  - lib/nxt_schema/validators/attribute.rb
169
- - lib/nxt_schema/validators/equality.rb
170
- - lib/nxt_schema/validators/excluded.rb
189
+ - lib/nxt_schema/validators/equal_to.rb
190
+ - lib/nxt_schema/validators/error_messages.rb
191
+ - lib/nxt_schema/validators/error_messages/en.yaml
192
+ - lib/nxt_schema/validators/excluded_in.rb
171
193
  - lib/nxt_schema/validators/excludes.rb
172
194
  - lib/nxt_schema/validators/greater_than.rb
173
195
  - lib/nxt_schema/validators/greater_than_or_equal.rb
174
- - lib/nxt_schema/validators/included.rb
196
+ - lib/nxt_schema/validators/included_in.rb
175
197
  - lib/nxt_schema/validators/includes.rb
176
198
  - lib/nxt_schema/validators/less_than.rb
177
199
  - lib/nxt_schema/validators/less_than_or_equal.rb
@@ -179,6 +201,7 @@ files:
179
201
  - lib/nxt_schema/validators/pattern.rb
180
202
  - lib/nxt_schema/validators/query.rb
181
203
  - lib/nxt_schema/validators/registry.rb
204
+ - lib/nxt_schema/validators/validate_with_proxy.rb
182
205
  - lib/nxt_schema/validators/validator.rb
183
206
  - lib/nxt_schema/version.rb
184
207
  - nxt_schema.gemspec
@@ -204,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
227
  - !ruby/object:Gem::Version
205
228
  version: '0'
206
229
  requirements: []
207
- rubygems_version: 3.0.3
230
+ rubygems_version: 3.1.2
208
231
  signing_key:
209
232
  specification_version: 4
210
233
  summary: Write a short summary, because RubyGems requires one.