dry-schema 1.3.0 → 1.4.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.
@@ -79,6 +79,15 @@ module Dry
79
79
  self
80
80
  end
81
81
 
82
+ # @api private
83
+ def cache_key(predicate, options)
84
+ if options[:locale]
85
+ super
86
+ else
87
+ [*super, I18n.locale]
88
+ end
89
+ end
90
+
82
91
  private
83
92
 
84
93
  # @api private
@@ -55,7 +55,7 @@ module Dry
55
55
  end
56
56
 
57
57
  # @api private
58
- def lookup_paths(tokens)
58
+ def filled_lookup_paths(tokens)
59
59
  super(tokens.merge(root: "#{tokens[:root]}.#{namespace}")) + super
60
60
  end
61
61
 
@@ -64,6 +64,11 @@ module Dry
64
64
  base_paths = messages.rule_lookup_paths(tokens)
65
65
  base_paths.map { |key| key.gsub('dry_schema', "dry_schema.#{namespace}") } + base_paths
66
66
  end
67
+
68
+ # @api private
69
+ def cache_key(predicate, options)
70
+ messages.cache_key(predicate, options)
71
+ end
67
72
  end
68
73
  end
69
74
  end
@@ -68,6 +68,18 @@ module Dry
68
68
  @t = proc { |key, locale: default_locale| get("%<locale>s.#{key}", locale: locale) }
69
69
  end
70
70
 
71
+ # Get an array of looked up paths
72
+ #
73
+ # @param [Symbol] predicate
74
+ # @param [Hash] options
75
+ #
76
+ # @return [String]
77
+ #
78
+ # @api public
79
+ def looked_up_paths(predicate, options)
80
+ super.map { |path| path % { locale: options[:locale] || default_locale } }
81
+ end
82
+
71
83
  # Get a message for the given key and its options
72
84
  #
73
85
  # @param [Symbol] key
@@ -29,9 +29,10 @@ module Dry
29
29
  end
30
30
 
31
31
  # @api private
32
- def ast(input=Undefined)
32
+ def ast(input = Undefined)
33
33
  [:namespace, [namespace, rule.ast(input)]]
34
34
  end
35
+ alias_method :to_ast, :ast
35
36
  end
36
37
  end
37
38
  end
@@ -65,8 +65,10 @@ module Dry
65
65
  # @api private
66
66
  def include?(other)
67
67
  return false unless same_root?(other)
68
- return false if index? && other.index? && !last.equal?(other.last)
69
- self >= other
68
+ return last.equal?(other.last) if index? && other.index?
69
+ return self.class.new([*to_a[0..-2]]).include?(other) if index?
70
+
71
+ self >= other && !other.key_matches(self).include?(nil)
70
72
  end
71
73
 
72
74
  # @api private
@@ -75,14 +77,16 @@ module Dry
75
77
 
76
78
  return 0 if keys.eql?(other.keys)
77
79
 
78
- res =
79
- map { |key| (idx = other.index(key)) && keys[idx].equal?(key) }
80
- .compact
81
- .reject { |value| value.equal?(false) }
80
+ res = key_matches(other).compact.reject { |value| value.equal?(false) }
82
81
 
83
82
  res.size < count ? 1 : -1
84
83
  end
85
84
 
85
+ # @api private
86
+ def key_matches(other)
87
+ map { |key| (idx = other.index(key)) && keys[idx].equal?(key) }
88
+ end
89
+
86
90
  # @api private
87
91
  def last
88
92
  keys.last
@@ -1,35 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/logic/predicates'
4
+ require 'dry/types/predicate_registry'
4
5
 
5
6
  module Dry
6
7
  module Schema
7
8
  # A registry with predicate objects from `Dry::Logic::Predicates`
8
9
  #
9
10
  # @api private
10
- class PredicateRegistry
11
- # @api private
12
- attr_reader :predicates
13
-
14
- # @api private
15
- attr_reader :has_predicate
16
-
17
- # @api private
18
- def initialize(predicates = Dry::Logic::Predicates)
19
- @predicates = predicates
20
- @has_predicate = ::Kernel.instance_method(:respond_to?).bind(@predicates)
21
- end
22
-
23
- # @api private
24
- def [](name)
25
- predicates[name]
26
- end
27
-
28
- # @api private
29
- def key?(name)
30
- has_predicate.(name)
31
- end
32
-
11
+ class PredicateRegistry < Dry::Types::PredicateRegistry
33
12
  # @api private
34
13
  def arg_list(name, *values)
35
14
  predicate = self[name]
@@ -5,6 +5,7 @@ require 'dry/initializer'
5
5
 
6
6
  require 'dry/schema/type_registry'
7
7
  require 'dry/schema/type_container'
8
+ require 'dry/schema/processor_steps'
8
9
  require 'dry/schema/rule_applier'
9
10
  require 'dry/schema/key_coercer'
10
11
  require 'dry/schema/value_coercer'
@@ -12,14 +13,9 @@ require 'dry/schema/value_coercer'
12
13
  module Dry
13
14
  module Schema
14
15
  # Processes input data using objects configured within the DSL
16
+ # Processing is split into steps represented by `ProcessorSteps`.
15
17
  #
16
- # Processing is split into 4 main steps:
17
- #
18
- # 1. Prepare input hash using a key map
19
- # 2. Apply pre-coercion filtering rules (optional step, used only when `filter` was used)
20
- # 3. Apply value coercions based on type specifications
21
- # 4. Apply rules
22
- #
18
+ # @see ProcessorSteps
23
19
  # @see Params
24
20
  # @see JSON
25
21
  #
@@ -29,10 +25,10 @@ module Dry
29
25
  extend Dry::Configurable
30
26
 
31
27
  setting :key_map_type
32
- setting :type_registry_namespace, :nominal
28
+ setting :type_registry_namespace, :strict
33
29
  setting :filter_empty_string, false
34
30
 
35
- option :steps, default: -> { EMPTY_ARRAY.dup }
31
+ option :steps, default: -> { ProcessorSteps.new }
36
32
 
37
33
  option :schema_dsl
38
34
 
@@ -77,16 +73,6 @@ module Dry
77
73
  end
78
74
  end
79
75
 
80
- # Append a step
81
- #
82
- # @return [Processor]
83
- #
84
- # @api private
85
- def <<(step)
86
- steps << step
87
- self
88
- end
89
-
90
76
  # Apply processing steps to the provided input
91
77
  #
92
78
  # @param [Hash] input
@@ -96,10 +82,7 @@ module Dry
96
82
  # @api public
97
83
  def call(input)
98
84
  Result.new(input, message_compiler: message_compiler) do |result|
99
- steps.each do |step|
100
- output = step.(result)
101
- result.replace(output) if output.is_a?(::Hash)
102
- end
85
+ steps.call(result)
103
86
  end
104
87
  end
105
88
  alias_method :[], :call
@@ -119,7 +102,7 @@ module Dry
119
102
  #
120
103
  # @api public
121
104
  def key_map
122
- @key_map ||= steps.detect { |s| s.is_a?(KeyCoercer) }.key_map
105
+ steps[:key_coercer].key_map
123
106
  end
124
107
 
125
108
  # Return string represntation
@@ -139,7 +122,7 @@ module Dry
139
122
  #
140
123
  # @api private
141
124
  def type_schema
142
- @type_schema ||= steps.detect { |s| s.is_a?(ValueCoercer) }.type_schema
125
+ steps[:value_coercer].type_schema
143
126
  end
144
127
 
145
128
  # Return the rules config
@@ -180,7 +163,7 @@ module Dry
180
163
  #
181
164
  # @api private
182
165
  def rule_applier
183
- @rule_applier ||= steps.last
166
+ steps[:rule_applier]
184
167
  end
185
168
  alias_method :to_rule, :rule_applier
186
169
 
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/initializer'
4
+
5
+ module Dry
6
+ module Schema
7
+ # Steps for the Dry::Schema::Processor
8
+ #
9
+ # There are 4 main steps:
10
+ #
11
+ # 1. `key_coercer` - Prepare input hash using a key map
12
+ # 2. `filter_schema` - Apply pre-coercion filtering rules
13
+ # (optional step, used only when `filter` was used)
14
+ # 3. `value_coercer` - Apply value coercions based on type specifications
15
+ # 4. `rule_applier` - Apply rules
16
+ #
17
+ # @see Processor
18
+ #
19
+ # @api public
20
+ class ProcessorSteps
21
+ extend Dry::Initializer
22
+
23
+ STEPS_IN_ORDER = %i[key_coercer filter_schema value_coercer rule_applier].freeze
24
+
25
+ option :steps, default: -> { EMPTY_HASH.dup }
26
+ option :before_steps, default: -> { EMPTY_HASH.dup }
27
+ option :after_steps, default: -> { EMPTY_HASH.dup }
28
+
29
+ def call(result)
30
+ STEPS_IN_ORDER.each do |name|
31
+ before_steps[name]&.each { |step| process_step(step, result) }
32
+ process_step(steps[name], result)
33
+ after_steps[name]&.each { |step| process_step(step, result) }
34
+ end
35
+ result
36
+ end
37
+
38
+ # Return step by name
39
+ #
40
+ # @param [Symbol] name The step name
41
+ #
42
+ # @api public
43
+ def [](name)
44
+ steps[name]
45
+ end
46
+
47
+ # Sets step by name
48
+ #
49
+ # @param [Symbol] name The step name
50
+ #
51
+ # @api public
52
+ def []=(name, value)
53
+ validate_step_name(name)
54
+ steps[name] = value
55
+ end
56
+
57
+ # Add passed block before mentioned step
58
+ #
59
+ # @param [Symbol] name The step name
60
+ #
61
+ # @return [ProcessorSteps]
62
+ #
63
+ # @api public
64
+ def after(name, &block)
65
+ validate_step_name(name)
66
+ after_steps[name] ||= EMPTY_ARRAY.dup
67
+ after_steps[name] << block.to_proc
68
+ self
69
+ end
70
+
71
+ # Add passed block before mentioned step
72
+ #
73
+ # @param [Symbol] name The step name
74
+ #
75
+ # @return [ProcessorSteps]
76
+ #
77
+ # @api public
78
+ def before(name, &block)
79
+ validate_step_name(name)
80
+ before_steps[name] ||= EMPTY_ARRAY.dup
81
+ before_steps[name] << block.to_proc
82
+ self
83
+ end
84
+
85
+ # @api private
86
+ def process_step(step, result)
87
+ return unless step
88
+
89
+ output = step.(result)
90
+ result.replace(output) if output.is_a?(::Hash)
91
+ end
92
+
93
+ # @api private
94
+ def validate_step_name(name)
95
+ return if STEPS_IN_ORDER.include?(name)
96
+
97
+ raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -18,12 +18,12 @@ module Dry
18
18
  attr_reader :namespace
19
19
 
20
20
  # @api private
21
- def self.new(types = Dry::Types, namespace = :nominal)
21
+ def self.new(types = Dry::Types, namespace = :strict)
22
22
  super
23
23
  end
24
24
 
25
25
  # @api private
26
- def initialize(types, namespace = :nominal)
26
+ def initialize(types, namespace = :strict)
27
27
  @types = types
28
28
  @namespace = namespace
29
29
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Schema
5
- VERSION = '1.3.0'
5
+ VERSION = '1.4.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-06 00:00:00.000000000 Z
11
+ date: 2019-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -106,14 +106,14 @@ dependencies:
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '1.0'
109
+ version: '1.2'
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '1.0'
116
+ version: '1.2'
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: bundler
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -217,8 +217,8 @@ files:
217
217
  - lib/dry/schema/predicate.rb
218
218
  - lib/dry/schema/predicate_inferrer.rb
219
219
  - lib/dry/schema/predicate_registry.rb
220
- - lib/dry/schema/primitive_inferrer.rb
221
220
  - lib/dry/schema/processor.rb
221
+ - lib/dry/schema/processor_steps.rb
222
222
  - lib/dry/schema/result.rb
223
223
  - lib/dry/schema/rule_applier.rb
224
224
  - lib/dry/schema/trace.rb
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'dry/core/cache'
4
-
5
- module Dry
6
- module Schema
7
- # PrimitiveInferrer is used internally by `Macros::Filled`
8
- # for inferring a list of possible primitives that a given
9
- # type can handle.
10
- #
11
- # @api private
12
- class PrimitiveInferrer
13
- extend Dry::Core::Cache
14
-
15
- # Compiler reduces type AST into a list of primitives
16
- #
17
- # @api private
18
- class Compiler
19
- # @api private
20
- def visit(node)
21
- meth, rest = node
22
- public_send(:"visit_#{meth}", rest)
23
- end
24
-
25
- # @api private
26
- def visit_nominal(node)
27
- type, _ = node
28
- type
29
- end
30
-
31
- # @api private
32
- def visit_hash(_)
33
- Hash
34
- end
35
-
36
- # @api private
37
- def visit_array(_)
38
- Array
39
- end
40
-
41
- # @api private
42
- def visit_lax(node)
43
- visit(node)
44
- end
45
-
46
- # @api private
47
- def visit_constructor(node)
48
- other, * = node
49
- visit(other)
50
- end
51
-
52
- # @api private
53
- def visit_enum(node)
54
- other, * = node
55
- visit(other)
56
- end
57
-
58
- # @api private
59
- def visit_sum(node)
60
- left, right = node
61
-
62
- [visit(left), visit(right)].flatten(1)
63
- end
64
-
65
- # @api private
66
- def visit_constrained(node)
67
- other, * = node
68
- visit(other)
69
- end
70
-
71
- # @api private
72
- def visit_any(_)
73
- Object
74
- end
75
- end
76
-
77
- # @return [Compiler]
78
- # @api private
79
- attr_reader :compiler
80
-
81
- # @api private
82
- def initialize
83
- @compiler = Compiler.new
84
- end
85
-
86
- # Infer predicate identifier from the provided type
87
- #
88
- # @return [Symbol]
89
- #
90
- # @api private
91
- def [](type)
92
- self.class.fetch_or_store(type.hash) do
93
- Array(compiler.visit(type.to_ast)).freeze
94
- end
95
- end
96
- end
97
- end
98
- end