dry-schema 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Join the chat at https://
|
7
|
+
# dry-schema [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
|
8
8
|
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/dry-schema.svg)][gem]
|
10
10
|
[![Build Status](https://travis-ci.org/dry-rb/dry-schema.svg?branch=master)][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
|
- - ">="
|