dry-schema 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +2 -2
- data/config/errors.yml +4 -0
- data/lib/dry/schema/compiler.rb +5 -0
- data/lib/dry/schema/dsl.rb +16 -16
- data/lib/dry/schema/extensions/hints.rb +3 -4
- data/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
- data/lib/dry/schema/extensions/hints/message_set_methods.rb +1 -1
- data/lib/dry/schema/extensions/monads.rb +3 -3
- data/lib/dry/schema/macros/array.rb +16 -3
- data/lib/dry/schema/macros/dsl.rb +51 -1
- data/lib/dry/schema/macros/each.rb +9 -0
- data/lib/dry/schema/macros/filled.rb +2 -2
- data/lib/dry/schema/macros/key.rb +0 -50
- data/lib/dry/schema/message.rb +16 -58
- data/lib/dry/schema/message/or.rb +52 -0
- data/lib/dry/schema/message_compiler.rb +12 -7
- data/lib/dry/schema/message_set.rb +18 -5
- data/lib/dry/schema/messages/abstract.rb +19 -10
- data/lib/dry/schema/messages/yaml.rb +17 -5
- data/lib/dry/schema/predicate_inferrer.rb +5 -0
- data/lib/dry/schema/result.rb +1 -1
- data/lib/dry/schema/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91cccc18d1227997d8a7d14d0e73372625e11f3180bf0dede6ec38dc60e9acde
|
4
|
+
data.tar.gz: d900bd76107b6ac3825a32ead9e0a9818426a96d7ee4b591c55a49e53b84e79f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7457d70d6e82e2691d067ebf33c9fa1512938856bfda50304f20010e2b3313804b37215acd972e659968af60dc317d2771e8efa017dc0b43072d3aef7008a9b
|
7
|
+
data.tar.gz: 81467419fdc896a52e9013677d17b0c9cfd91e16ea4323efb007554899eb69e43a7c20b5426eb5213687631ed5e04e34f79fbf780e9e9d4748a1971faa4385c0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,43 @@
|
|
1
|
+
# 0.5.0 2019-04-04
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for arbitrary meta-data in messages, ie:
|
6
|
+
|
7
|
+
```yaml
|
8
|
+
en:
|
9
|
+
dry_schema:
|
10
|
+
errors:
|
11
|
+
filled?:
|
12
|
+
text: "cannot be blank"
|
13
|
+
code: 123
|
14
|
+
```
|
15
|
+
|
16
|
+
Now your error hash will include `{ foo: [{ text: 'cannot be blank', code: 123 }] }` (solnic + flash-gordon)
|
17
|
+
|
18
|
+
* Support for type specs in `array` macro, ie `required(:tags).array(:integer)` (solnic)
|
19
|
+
* Support for type specs in `each` macro, ie `required(:tags).each(:integer)` (solnic)
|
20
|
+
* Shortcut for defining an array with hash as its member, ie:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
Dry::Schema.Params do
|
24
|
+
required(:tags).array(:hash) do
|
25
|
+
required(:name).filled(:string)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
|
32
|
+
* Inferring type specs when type is already set works correctly (solnic)
|
33
|
+
|
34
|
+
### Changed
|
35
|
+
|
36
|
+
* [BREAKING] `:monads` extension wraps entire result objects in `Success` or `Failure` (flash-gordon)
|
37
|
+
* When `:hints` are disabled, result AST will not include hint nodes (solnic)
|
38
|
+
|
39
|
+
[Compare v0.4.0...v0.5.0](https://github.com/dry-rb/dry-schema/compare/v0.4.0...v0.5.0)
|
40
|
+
|
1
41
|
# 0.4.0 2019-03-26
|
2
42
|
|
3
43
|
### Added
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-schema
|
2
2
|
[travis]: https://travis-ci.org/dry-rb/dry-schema
|
3
3
|
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-schema
|
4
|
-
[
|
4
|
+
[chat]: https://dry-rb.zulipchat.com
|
5
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-schema
|
6
6
|
|
7
|
-
# dry-schema [][chat]
|
8
8
|
|
9
9
|
[][gem]
|
10
10
|
[][travis]
|
data/config/errors.yml
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
en:
|
2
2
|
dry_schema:
|
3
3
|
or: "or"
|
4
|
+
|
4
5
|
errors:
|
5
6
|
array?: "must be an array"
|
6
7
|
|
@@ -12,6 +13,7 @@ en:
|
|
12
13
|
arg:
|
13
14
|
default: "must not be one of: %{list}"
|
14
15
|
range: "must not be one of: %{list_left} - %{list_right}"
|
16
|
+
|
15
17
|
exclusion?: "must not be one of: %{list}"
|
16
18
|
|
17
19
|
eql?: "must be equal to %{left}"
|
@@ -38,6 +40,7 @@ en:
|
|
38
40
|
arg:
|
39
41
|
default: "must be one of: %{list}"
|
40
42
|
range: "must be one of: %{list_left} - %{list_right}"
|
43
|
+
|
41
44
|
inclusion?: "must be one of: %{list}"
|
42
45
|
|
43
46
|
includes?: "must include %{value}"
|
@@ -88,5 +91,6 @@ en:
|
|
88
91
|
arg:
|
89
92
|
default: "length must be %{size}"
|
90
93
|
range: "length must be within %{size_left} - %{size_right}"
|
94
|
+
|
91
95
|
not:
|
92
96
|
empty?: "cannot be empty"
|
data/lib/dry/schema/compiler.rb
CHANGED
data/lib/dry/schema/dsl.rb
CHANGED
@@ -247,6 +247,22 @@ module Dry
|
|
247
247
|
types[name] = type.meta(meta)
|
248
248
|
end
|
249
249
|
|
250
|
+
# Resolve type object from the provided spec
|
251
|
+
#
|
252
|
+
# @param [Symbol, Array<Symbol>, Dry::Types::Type]
|
253
|
+
#
|
254
|
+
# @return [Dry::Types::Type]
|
255
|
+
#
|
256
|
+
# @api private
|
257
|
+
def resolve_type(spec)
|
258
|
+
case spec
|
259
|
+
when ::Dry::Types::Type then spec
|
260
|
+
when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
|
261
|
+
else
|
262
|
+
type_registry[spec]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
250
266
|
protected
|
251
267
|
|
252
268
|
# Build a rule applier
|
@@ -345,22 +361,6 @@ module Dry
|
|
345
361
|
end
|
346
362
|
end
|
347
363
|
|
348
|
-
# Resolve type object from the provided spec
|
349
|
-
#
|
350
|
-
# @param [Symbol, Array<Symbol>, Dry::Types::Type]
|
351
|
-
#
|
352
|
-
# @return [Dry::Types::Type]
|
353
|
-
#
|
354
|
-
# @api private
|
355
|
-
def resolve_type(spec)
|
356
|
-
case spec
|
357
|
-
when ::Dry::Types::Type then spec
|
358
|
-
when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
|
359
|
-
else
|
360
|
-
type_registry[spec]
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
364
|
# @api private
|
365
365
|
def parent_rules
|
366
366
|
parent&.rules || EMPTY_HASH
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'dry/schema/compiler'
|
3
4
|
require 'dry/schema/message'
|
4
5
|
require 'dry/schema/message_compiler'
|
5
6
|
|
7
|
+
require 'dry/schema/extensions/hints/compiler_methods'
|
6
8
|
require 'dry/schema/extensions/hints/message_compiler_methods'
|
7
9
|
require 'dry/schema/extensions/hints/message_set_methods'
|
8
10
|
require 'dry/schema/extensions/hints/result_methods'
|
@@ -35,10 +37,6 @@ module Dry
|
|
35
37
|
#
|
36
38
|
# @api private
|
37
39
|
class Hint < Message
|
38
|
-
def self.[](predicate, path, text, options)
|
39
|
-
Hint.new(predicate, path, text, options)
|
40
|
-
end
|
41
|
-
|
42
40
|
# @api private
|
43
41
|
def hint?
|
44
42
|
true
|
@@ -46,6 +44,7 @@ module Dry
|
|
46
44
|
end
|
47
45
|
|
48
46
|
module Extensions
|
47
|
+
Compiler.prepend(Hints::CompilerMethods)
|
49
48
|
MessageCompiler.prepend(Hints::MessageCompilerMethods)
|
50
49
|
MessageSet.prepend(Hints::MessageSetMethods)
|
51
50
|
Result.prepend(Hints::ResultMethods)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Schema
|
5
|
+
module Extensions
|
6
|
+
module Hints
|
7
|
+
# Tweaks AND visitor to enable :hints
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module CompilerMethods
|
11
|
+
# @api private
|
12
|
+
def visit_and(node)
|
13
|
+
super.with(hints: true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -7,11 +7,11 @@ module Dry
|
|
7
7
|
class Result
|
8
8
|
include Dry::Monads::Result::Mixin
|
9
9
|
|
10
|
-
def to_monad
|
10
|
+
def to_monad
|
11
11
|
if success?
|
12
|
-
Success(
|
12
|
+
Success(self)
|
13
13
|
else
|
14
|
-
Failure(
|
14
|
+
Failure(self)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -10,9 +10,22 @@ module Dry
|
|
10
10
|
# @api public
|
11
11
|
class Array < DSL
|
12
12
|
# @api private
|
13
|
-
def value(*args, &block)
|
14
|
-
|
15
|
-
|
13
|
+
def value(*args, **opts, &block)
|
14
|
+
type(:array)
|
15
|
+
|
16
|
+
extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
|
17
|
+
type(schema_dsl.array[type_spec]) if type_spec
|
18
|
+
|
19
|
+
is_hash_block = type_spec.equal?(:hash)
|
20
|
+
|
21
|
+
if predicates.any? || opts.any? || !is_hash_block
|
22
|
+
super(*predicates, type_spec: type_spec, **opts, &(is_hash_block ? nil : block))
|
23
|
+
end
|
24
|
+
|
25
|
+
hash(&block) if is_hash_block
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
16
29
|
end
|
17
30
|
|
18
31
|
# @api private
|
@@ -19,6 +19,11 @@ module Dry
|
|
19
19
|
# @api private
|
20
20
|
option :chain, default: -> { true }
|
21
21
|
|
22
|
+
# @!attribute [r] predicate_inferrer
|
23
|
+
# @return [PredicateInferrer]
|
24
|
+
# @api private
|
25
|
+
option :predicate_inferrer, default: proc { PredicateInferrer.new(compiler.predicates) }
|
26
|
+
|
22
27
|
# Specify predicates that should be applied to a value
|
23
28
|
#
|
24
29
|
# @api public
|
@@ -75,10 +80,22 @@ module Dry
|
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
83
|
+
# Set type spec
|
84
|
+
#
|
85
|
+
# @param [Symbol, Array, Dry::Types::Type]
|
86
|
+
#
|
87
|
+
# @return [Macros::Key]
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def type(spec)
|
91
|
+
schema_dsl.set_type(name, spec)
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
78
95
|
private
|
79
96
|
|
80
97
|
# @api private
|
81
|
-
def append_macro(macro_type
|
98
|
+
def append_macro(macro_type)
|
82
99
|
macro = macro_type.new(schema_dsl: schema_dsl, name: name)
|
83
100
|
|
84
101
|
yield(macro)
|
@@ -90,6 +107,39 @@ module Dry
|
|
90
107
|
macro
|
91
108
|
end
|
92
109
|
end
|
110
|
+
|
111
|
+
# @api private
|
112
|
+
def extract_type_spec(*args, nullable: false, set_type: true)
|
113
|
+
type_spec = args[0]
|
114
|
+
|
115
|
+
is_type_spec = type_spec.is_a?(Dry::Schema::Processor) ||
|
116
|
+
type_spec.is_a?(Symbol) &&
|
117
|
+
type_spec.to_s.end_with?(QUESTION_MARK)
|
118
|
+
|
119
|
+
type_spec = nil if is_type_spec
|
120
|
+
|
121
|
+
predicates = Array(type_spec ? args[1..-1] : args)
|
122
|
+
|
123
|
+
if type_spec
|
124
|
+
resolved_type = schema_dsl.resolve_type(
|
125
|
+
nullable && !type_spec.is_a?(::Array) ? [:nil, type_spec] : type_spec
|
126
|
+
)
|
127
|
+
|
128
|
+
type(resolved_type) if set_type
|
129
|
+
|
130
|
+
type_predicates = predicate_inferrer[resolved_type]
|
131
|
+
|
132
|
+
unless predicates.include?(type_predicates)
|
133
|
+
if type_predicates.is_a?(::Array) && type_predicates.size.equal?(1)
|
134
|
+
predicates.unshift(type_predicates[0])
|
135
|
+
else
|
136
|
+
predicates.unshift(type_predicates)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
yield(*predicates, type_spec: type_spec)
|
142
|
+
end
|
93
143
|
end
|
94
144
|
end
|
95
145
|
end
|
@@ -9,6 +9,15 @@ module Dry
|
|
9
9
|
#
|
10
10
|
# @api public
|
11
11
|
class Each < DSL
|
12
|
+
# @api private
|
13
|
+
def value(*args, **opts)
|
14
|
+
extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
|
15
|
+
type(schema_dsl.array[type_spec]) if type_spec
|
16
|
+
|
17
|
+
super(*predicates, type_spec: type_spec, **opts)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
12
21
|
# @api private
|
13
22
|
def to_ast(*)
|
14
23
|
[:each, trace.to_ast]
|
@@ -18,8 +18,8 @@ module Dry
|
|
18
18
|
raise ::Dry::Schema::InvalidSchemaError, 'Using filled with filled? is redundant'
|
19
19
|
end
|
20
20
|
|
21
|
-
if opts[:type_spec]
|
22
|
-
value(predicates[0], :filled?, *predicates[1..predicates.size-1], **opts, &block)
|
21
|
+
if opts[:type_spec]
|
22
|
+
value(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts, &block)
|
23
23
|
else
|
24
24
|
value(:filled?, *predicates, **opts, &block)
|
25
25
|
end
|
@@ -19,13 +19,6 @@ module Dry
|
|
19
19
|
# @api private
|
20
20
|
option :filter_schema, optional: true, default: proc { schema_dsl&.new }
|
21
21
|
|
22
|
-
# @!attribute [r] predicate_inferrer
|
23
|
-
# @return [PredicateInferrer]
|
24
|
-
# @api private
|
25
|
-
option :predicate_inferrer, default: proc {
|
26
|
-
PredicateInferrer.new(compiler.predicates)
|
27
|
-
}
|
28
|
-
|
29
22
|
# Specify predicates that should be used to filter out values
|
30
23
|
# before coercion is applied
|
31
24
|
#
|
@@ -80,18 +73,6 @@ module Dry
|
|
80
73
|
end
|
81
74
|
end
|
82
75
|
|
83
|
-
# Set type spec
|
84
|
-
#
|
85
|
-
# @param [Symbol, Array, Dry::Types::Type]
|
86
|
-
#
|
87
|
-
# @return [Macros::Key]
|
88
|
-
#
|
89
|
-
# @api public
|
90
|
-
def type(spec)
|
91
|
-
schema_dsl.set_type(name, spec)
|
92
|
-
self
|
93
|
-
end
|
94
|
-
|
95
76
|
# Coerce macro to a rule
|
96
77
|
#
|
97
78
|
# @return [Dry::Logic::Rule]
|
@@ -109,37 +90,6 @@ module Dry
|
|
109
90
|
def to_ast
|
110
91
|
[:predicate, [:key?, [[:name, name], [:input, Undefined]]]]
|
111
92
|
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
# @api private
|
116
|
-
def extract_type_spec(*args, nullable: false)
|
117
|
-
type_spec = args[0]
|
118
|
-
|
119
|
-
is_type_spec = type_spec.kind_of?(Dry::Schema::Processor) ||
|
120
|
-
type_spec.is_a?(Symbol) &&
|
121
|
-
type_spec.to_s.end_with?(QUESTION_MARK)
|
122
|
-
|
123
|
-
type_spec = nil if is_type_spec
|
124
|
-
|
125
|
-
predicates = Array(type_spec ? args[1..-1] : args)
|
126
|
-
|
127
|
-
if type_spec
|
128
|
-
type(nullable && !type_spec.is_a?(::Array) ? [:nil, type_spec] : type_spec)
|
129
|
-
|
130
|
-
type_predicates = predicate_inferrer[schema_dsl.types[name]]
|
131
|
-
|
132
|
-
unless predicates.include?(type_predicates)
|
133
|
-
if type_predicates.is_a?(::Array) && type_predicates.size.equal?(1)
|
134
|
-
predicates.unshift(type_predicates[0])
|
135
|
-
else
|
136
|
-
predicates.unshift(type_predicates)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
yield(*predicates, type_spec: !type_spec.nil?)
|
142
|
-
end
|
143
93
|
end
|
144
94
|
end
|
145
95
|
end
|
data/lib/dry/schema/message.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'dry/initializer'
|
3
4
|
require 'dry/equalizer'
|
5
|
+
|
4
6
|
require 'dry/schema/path'
|
7
|
+
require 'dry/schema/message/or'
|
5
8
|
|
6
9
|
module Dry
|
7
10
|
module Schema
|
@@ -9,76 +12,31 @@ module Dry
|
|
9
12
|
#
|
10
13
|
# @api public
|
11
14
|
class Message
|
12
|
-
include Dry::Equalizer(:
|
15
|
+
include Dry::Equalizer(:text, :path, :predicate, :input)
|
13
16
|
|
14
|
-
|
17
|
+
extend Dry::Initializer
|
15
18
|
|
16
|
-
|
17
|
-
#
|
18
|
-
# @api public
|
19
|
-
class Or
|
20
|
-
include Enumerable
|
19
|
+
option :text
|
21
20
|
|
22
|
-
|
23
|
-
attr_reader :left
|
21
|
+
option :path
|
24
22
|
|
25
|
-
|
26
|
-
attr_reader :right
|
23
|
+
option :predicate
|
27
24
|
|
28
|
-
|
29
|
-
attr_reader :path
|
25
|
+
option :args, default: proc { EMPTY_ARRAY }
|
30
26
|
|
31
|
-
|
32
|
-
attr_reader :messages
|
27
|
+
option :input
|
33
28
|
|
34
|
-
|
35
|
-
def initialize(left, right, messages)
|
36
|
-
@left = left
|
37
|
-
@right = right
|
38
|
-
@messages = messages
|
39
|
-
@path = left.path
|
40
|
-
end
|
29
|
+
option :meta, optional: true, default: proc { EMPTY_HASH }
|
41
30
|
|
42
|
-
|
43
|
-
#
|
44
|
-
# @api public
|
45
|
-
def to_s
|
46
|
-
uniq.join(" #{messages[:or]} ")
|
47
|
-
end
|
48
|
-
|
49
|
-
# @api private
|
50
|
-
def each(&block)
|
51
|
-
to_a.each(&block)
|
52
|
-
end
|
53
|
-
|
54
|
-
# @api private
|
55
|
-
def to_a
|
56
|
-
[left, right]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Build a new message object
|
31
|
+
# Dump the message to a representation suitable for the message set hash
|
61
32
|
#
|
62
|
-
# @
|
63
|
-
def self.[](predicate, path, text, options)
|
64
|
-
Message.new(predicate, path, text, options)
|
65
|
-
end
|
66
|
-
|
67
|
-
# @api private
|
68
|
-
def initialize(predicate, path, text, options)
|
69
|
-
@predicate = predicate
|
70
|
-
@path = path
|
71
|
-
@text = text
|
72
|
-
@options = options
|
73
|
-
@args = options[:args] || EMPTY_ARRAY
|
74
|
-
end
|
75
|
-
|
76
|
-
# Return a string representation of the message
|
33
|
+
# @return [String,Hash]
|
77
34
|
#
|
78
35
|
# @api public
|
79
|
-
def
|
80
|
-
text
|
36
|
+
def dump
|
37
|
+
@dump ||= meta.empty? ? text : { text: text, **meta }
|
81
38
|
end
|
39
|
+
alias to_s dump
|
82
40
|
|
83
41
|
# @api private
|
84
42
|
def eql?(other)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/equalizer'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Schema
|
7
|
+
# Message objects used by message sets
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Message
|
11
|
+
# A message sub-type used by OR operations
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Or
|
15
|
+
# @api private
|
16
|
+
attr_reader :left
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
attr_reader :right
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
attr_reader :path
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
attr_reader :messages
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def initialize(left, right, messages)
|
29
|
+
@left = left
|
30
|
+
@right = right
|
31
|
+
@messages = messages
|
32
|
+
@path = left.path
|
33
|
+
end
|
34
|
+
|
35
|
+
# @see Message#dump
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def dump
|
41
|
+
to_a.map(&:dump).join(" #{messages[:or][:text]} ")
|
42
|
+
end
|
43
|
+
alias to_s dump
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def to_a
|
47
|
+
[left, right]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -28,6 +28,7 @@ module Dry
|
|
28
28
|
.new(resolve_predicate).update(key?: resolve_key_predicate).freeze
|
29
29
|
|
30
30
|
EMPTY_OPTS = VisitorOpts.new
|
31
|
+
EMPTY_MESSAGE_SET = MessageSet.new(EMPTY_ARRAY).freeze
|
31
32
|
|
32
33
|
param :messages
|
33
34
|
|
@@ -50,11 +51,17 @@ module Dry
|
|
50
51
|
def with(new_options)
|
51
52
|
return self if new_options.empty?
|
52
53
|
|
53
|
-
|
54
|
+
updated_opts = options.merge(new_options)
|
55
|
+
|
56
|
+
return self if updated_opts.eql?(options)
|
57
|
+
|
58
|
+
self.class.new(messages, updated_opts)
|
54
59
|
end
|
55
60
|
|
56
61
|
# @api private
|
57
62
|
def call(ast)
|
63
|
+
return EMPTY_MESSAGE_SET if ast.empty?
|
64
|
+
|
58
65
|
current_messages = EMPTY_ARRAY.dup
|
59
66
|
compiled_messages = ast.map { |node| visit(node, EMPTY_OPTS.dup(current_messages)) }
|
60
67
|
|
@@ -123,15 +130,13 @@ module Dry
|
|
123
130
|
path: path.last, **tokens, **lookup_options(arg_vals: arg_vals, input: input)
|
124
131
|
).to_h
|
125
132
|
|
126
|
-
template = messages[predicate, options] || raise(MissingMessageError, path)
|
133
|
+
template, meta = messages[predicate, options] || raise(MissingMessageError, path)
|
127
134
|
|
128
135
|
text = message_text(template, tokens, options)
|
129
136
|
|
130
|
-
message_type(options)
|
131
|
-
|
132
|
-
|
133
|
-
input: input
|
134
|
-
]
|
137
|
+
message_type(options).new(
|
138
|
+
text: text, path: path, predicate: predicate, args: arg_vals, input: input, meta: meta
|
139
|
+
)
|
135
140
|
end
|
136
141
|
|
137
142
|
# @api private
|
@@ -29,13 +29,15 @@ module Dry
|
|
29
29
|
|
30
30
|
# @api public
|
31
31
|
def each(&block)
|
32
|
+
return self if empty?
|
32
33
|
return to_enum unless block
|
34
|
+
|
33
35
|
messages.each(&block)
|
34
36
|
end
|
35
37
|
|
36
38
|
# @api public
|
37
39
|
def to_h
|
38
|
-
messages_map
|
40
|
+
@to_h ||= messages_map
|
39
41
|
end
|
40
42
|
alias_method :to_hash, :to_h
|
41
43
|
|
@@ -51,13 +53,22 @@ module Dry
|
|
51
53
|
|
52
54
|
# @api private
|
53
55
|
def empty?
|
54
|
-
messages.empty?
|
56
|
+
@empty ||= messages.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
def freeze
|
61
|
+
to_h
|
62
|
+
empty?
|
63
|
+
super
|
55
64
|
end
|
56
65
|
|
57
66
|
private
|
58
67
|
|
59
68
|
# @api private
|
60
69
|
def messages_map(messages = self.messages)
|
70
|
+
return EMPTY_HASH if empty?
|
71
|
+
|
61
72
|
messages.group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
|
62
73
|
node = path.reduce(hash) { |a, e| a[e] }
|
63
74
|
|
@@ -65,7 +76,7 @@ module Dry
|
|
65
76
|
node << msg
|
66
77
|
end
|
67
78
|
|
68
|
-
node.map!(&:
|
79
|
+
node.map!(&:dump)
|
69
80
|
|
70
81
|
hash
|
71
82
|
end
|
@@ -78,12 +89,14 @@ module Dry
|
|
78
89
|
|
79
90
|
# @api private
|
80
91
|
def initialize_placeholders!
|
81
|
-
@placeholders =
|
92
|
+
return @placeholders = EMPTY_HASH if empty?
|
93
|
+
|
94
|
+
@placeholders = paths.reduce(EMPTY_HASH.dup) do |hash, path|
|
82
95
|
curr_idx = 0
|
83
96
|
last_idx = path.size - 1
|
84
97
|
node = hash
|
85
98
|
|
86
|
-
while curr_idx <= last_idx
|
99
|
+
while curr_idx <= last_idx
|
87
100
|
key = path[curr_idx]
|
88
101
|
node = (node[key] || node[key] = curr_idx < last_idx ? {} : [])
|
89
102
|
curr_idx += 1
|
@@ -27,7 +27,6 @@ module Dry
|
|
27
27
|
'%<root>s.rules.%<path>s.%<predicate>s.arg.%<arg_type>s',
|
28
28
|
'%<root>s.rules.%<path>s.%<predicate>s',
|
29
29
|
'%<root>s.%<predicate>s.%<message_type>s',
|
30
|
-
'%<root>s.%<predicate>s.value.%<path>s.arg.%<arg_type>s',
|
31
30
|
'%<root>s.%<predicate>s.value.%<path>s',
|
32
31
|
'%<root>s.%<predicate>s.value.%<val_type>s.arg.%<arg_type>s',
|
33
32
|
'%<root>s.%<predicate>s.value.%<val_type>s',
|
@@ -87,7 +86,8 @@ module Dry
|
|
87
86
|
tokens = { name: name, locale: options.fetch(:locale, default_locale) }
|
88
87
|
path = rule_lookup_paths(tokens).detect { |key| key?(key, options) }
|
89
88
|
|
90
|
-
get(path, options) if path
|
89
|
+
rule = get(path, options) if path
|
90
|
+
rule.is_a?(Hash) ? rule[:text] : rule
|
91
91
|
end
|
92
92
|
|
93
93
|
# Retrieve a message template
|
@@ -97,8 +97,8 @@ module Dry
|
|
97
97
|
# @api public
|
98
98
|
def call(*args)
|
99
99
|
cache.fetch_or_store(args.hash) do
|
100
|
-
|
101
|
-
Template[
|
100
|
+
text, meta = lookup(*args)
|
101
|
+
[Template[text], meta] if text
|
102
102
|
end
|
103
103
|
end
|
104
104
|
alias_method :[], :call
|
@@ -106,10 +106,12 @@ module Dry
|
|
106
106
|
# Try to find a message for the given predicate and its options
|
107
107
|
#
|
108
108
|
# @api private
|
109
|
-
|
109
|
+
#
|
110
|
+
# rubocop:disable Metrics/AbcSize
|
111
|
+
def lookup(predicate, options)
|
110
112
|
tokens = options.merge(
|
111
|
-
root: options[:not] ? "#{root}.not" : root,
|
112
113
|
predicate: predicate,
|
114
|
+
root: options[:not] ? "#{root}.not" : root,
|
113
115
|
arg_type: config.arg_types[options[:arg_type]],
|
114
116
|
val_type: config.val_types[options[:val_type]],
|
115
117
|
message_type: options[:message_type] || :failure
|
@@ -117,12 +119,19 @@ module Dry
|
|
117
119
|
|
118
120
|
opts = options.reject { |k, _| config.lookup_options.include?(k) }
|
119
121
|
|
120
|
-
path = lookup_paths(tokens).detect
|
121
|
-
key?(key, opts) && get(key, opts).is_a?(String)
|
122
|
-
end
|
122
|
+
path = lookup_paths(tokens).detect { |key| key?(key, opts) }
|
123
123
|
|
124
|
-
|
124
|
+
return unless path
|
125
|
+
|
126
|
+
text = get(path, opts)
|
127
|
+
|
128
|
+
if text.is_a?(Hash)
|
129
|
+
text.values_at(:text, :meta)
|
130
|
+
else
|
131
|
+
[text, EMPTY_HASH]
|
132
|
+
end
|
125
133
|
end
|
134
|
+
# rubocop:enable Metrics/AbcSize
|
126
135
|
|
127
136
|
# @api private
|
128
137
|
def lookup_paths(tokens)
|
@@ -31,11 +31,23 @@ module Dry
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# @api private
|
34
|
-
def self.flat_hash(hash,
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def self.flat_hash(hash, path = [], keys = {})
|
35
|
+
hash.each do |key, value|
|
36
|
+
flat_hash(value, [*path, key], keys) if value.is_a?(Hash)
|
37
|
+
|
38
|
+
if value.is_a?(String) && hash['text'] != value
|
39
|
+
keys[[*path, key].join(DOT)] = {
|
40
|
+
text: value,
|
41
|
+
meta: EMPTY_HASH
|
42
|
+
}
|
43
|
+
elsif value.is_a?(Hash) && value['text'].is_a?(String)
|
44
|
+
keys[[*path, key].join(DOT)] = {
|
45
|
+
text: value['text'],
|
46
|
+
meta: value.dup.delete_if { |k| k == 'text' }.map { |k, v| [k.to_sym, v] }.to_h
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
keys
|
39
51
|
end
|
40
52
|
|
41
53
|
# @api private
|
data/lib/dry/schema/result.rb
CHANGED
data/lib/dry/schema/version.rb
CHANGED
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: 0.
|
4
|
+
version: 0.5.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-
|
11
|
+
date: 2019-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -98,20 +98,20 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ">="
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 0.
|
101
|
+
version: 0.6.0
|
102
102
|
- - "~>"
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '0.
|
104
|
+
version: '0.6'
|
105
105
|
type: :runtime
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: 0.
|
111
|
+
version: 0.6.0
|
112
112
|
- - "~>"
|
113
113
|
- !ruby/object:Gem::Version
|
114
|
-
version: '0.
|
114
|
+
version: '0.6'
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
116
|
name: dry-types
|
117
117
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,6 +193,7 @@ files:
|
|
193
193
|
- lib/dry/schema/dsl.rb
|
194
194
|
- lib/dry/schema/extensions.rb
|
195
195
|
- lib/dry/schema/extensions/hints.rb
|
196
|
+
- lib/dry/schema/extensions/hints/compiler_methods.rb
|
196
197
|
- lib/dry/schema/extensions/hints/message_compiler_methods.rb
|
197
198
|
- lib/dry/schema/extensions/hints/message_set_methods.rb
|
198
199
|
- lib/dry/schema/extensions/hints/result_methods.rb
|
@@ -215,6 +216,7 @@ files:
|
|
215
216
|
- lib/dry/schema/macros/schema.rb
|
216
217
|
- lib/dry/schema/macros/value.rb
|
217
218
|
- lib/dry/schema/message.rb
|
219
|
+
- lib/dry/schema/message/or.rb
|
218
220
|
- lib/dry/schema/message_compiler.rb
|
219
221
|
- lib/dry/schema/message_compiler/visitor_opts.rb
|
220
222
|
- lib/dry/schema/message_set.rb
|
@@ -250,7 +252,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
250
252
|
requirements:
|
251
253
|
- - ">="
|
252
254
|
- !ruby/object:Gem::Version
|
253
|
-
version: '2.
|
255
|
+
version: '2.4'
|
254
256
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
255
257
|
requirements:
|
256
258
|
- - ">="
|