datacaster 2.0.2 → 3.1.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/README.md +604 -287
- data/config/locales/en.yml +25 -0
- data/datacaster.gemspec +3 -1
- data/lib/datacaster/absent.rb +4 -0
- data/lib/datacaster/and_node.rb +3 -5
- data/lib/datacaster/and_with_error_aggregation_node.rb +5 -6
- data/lib/datacaster/array_schema.rb +18 -16
- data/lib/datacaster/base.rb +33 -44
- data/lib/datacaster/caster.rb +4 -8
- data/lib/datacaster/checker.rb +8 -10
- data/lib/datacaster/comparator.rb +9 -9
- data/lib/datacaster/config.rb +28 -0
- data/lib/datacaster/context_node.rb +43 -0
- data/lib/datacaster/context_nodes/errors_caster.rb +21 -0
- data/lib/datacaster/context_nodes/i18n.rb +20 -0
- data/lib/datacaster/context_nodes/i18n_keys_mapper.rb +27 -0
- data/lib/datacaster/context_nodes/pass_if.rb +11 -0
- data/lib/datacaster/context_nodes/structure_cleaner.rb +103 -0
- data/lib/datacaster/context_nodes/user_context.rb +20 -0
- data/lib/datacaster/definition_dsl.rb +36 -0
- data/lib/datacaster/hash_mapper.rb +13 -16
- data/lib/datacaster/hash_schema.rb +14 -15
- data/lib/datacaster/i18n_values/base.rb +87 -0
- data/lib/datacaster/i18n_values/key.rb +34 -0
- data/lib/datacaster/i18n_values/scope.rb +28 -0
- data/lib/datacaster/message_keys_merger.rb +8 -15
- data/lib/datacaster/or_node.rb +3 -4
- data/lib/datacaster/predefined.rb +150 -65
- data/lib/datacaster/result.rb +39 -18
- data/lib/datacaster/runtimes/base.rb +47 -0
- data/lib/datacaster/runtimes/i18n.rb +20 -0
- data/lib/datacaster/runtimes/structure_cleaner.rb +47 -0
- data/lib/datacaster/runtimes/user_context.rb +39 -0
- data/lib/datacaster/substitute_i18n.rb +48 -0
- data/lib/datacaster/switch_node.rb +72 -0
- data/lib/datacaster/then_node.rb +7 -8
- data/lib/datacaster/transformer.rb +4 -8
- data/lib/datacaster/trier.rb +9 -11
- data/lib/datacaster/validator.rb +8 -9
- data/lib/datacaster/version.rb +1 -1
- data/lib/datacaster.rb +15 -35
- metadata +60 -10
- data/lib/datacaster/definition_context.rb +0 -20
- data/lib/datacaster/terminator.rb +0 -98
@@ -13,21 +13,18 @@ module Datacaster
|
|
13
13
|
@fields = fields
|
14
14
|
end
|
15
15
|
|
16
|
-
def cast(object)
|
17
|
-
object = super(object)
|
18
|
-
# return Datacaster.ErrorResult(["must be hash"]) unless object.value.is_a?(Hash)
|
19
|
-
|
20
|
-
checked_schema = object.meta[:checked_schema].dup || {}
|
21
|
-
|
16
|
+
def cast(object, runtime:)
|
22
17
|
errors = {}
|
23
18
|
result = {}
|
24
19
|
|
20
|
+
runtime.will_check!
|
21
|
+
|
25
22
|
@fields.each do |key, validator|
|
26
|
-
new_value = validator.(object)
|
23
|
+
new_value = runtime.ignore_checks! { validator.with_runtime(runtime).(object) }
|
27
24
|
|
28
25
|
# transform_to_hash([:a, :b, :c] => pick(:a, :b, :c) & ...)
|
29
26
|
if key.is_a?(Array)
|
30
|
-
unwrapped = new_value.valid? ? new_value.value : new_value.
|
27
|
+
unwrapped = new_value.valid? ? new_value.value : new_value.raw_errors
|
31
28
|
|
32
29
|
if key.length != unwrapped.length
|
33
30
|
raise TypeError.new("When using transform_to_hash([:a, :b, :c] => validator), validator should return Array "\
|
@@ -40,17 +37,17 @@ module Datacaster
|
|
40
37
|
if key.is_a?(Array)
|
41
38
|
key.zip(new_value.value) do |new_key, new_key_value|
|
42
39
|
result[new_key] = new_key_value
|
43
|
-
|
40
|
+
runtime.checked_key!(new_key)
|
44
41
|
end
|
45
42
|
else
|
46
43
|
result[key] = new_value.value
|
47
|
-
|
44
|
+
runtime.checked_key!(key)
|
48
45
|
end
|
49
46
|
else
|
50
47
|
if key.is_a?(Array)
|
51
|
-
errors = self.class.merge_errors(errors, key.zip(new_value.
|
48
|
+
errors = self.class.merge_errors(errors, key.zip(new_value.raw_errors).to_h)
|
52
49
|
else
|
53
|
-
errors = self.class.merge_errors(errors, {key => new_value.
|
50
|
+
errors = self.class.merge_errors(errors, {key => new_value.raw_errors})
|
54
51
|
end
|
55
52
|
end
|
56
53
|
end
|
@@ -58,17 +55,17 @@ module Datacaster
|
|
58
55
|
errors.delete_if { |_, v| v.empty? }
|
59
56
|
|
60
57
|
if errors.empty?
|
61
|
-
# All unchecked key-value pairs of initial hash are passed through, and eliminated by
|
58
|
+
# All unchecked key-value pairs of initial hash are passed through, and eliminated by ContextNode
|
62
59
|
# at the end of the chain. If we weren't dealing with the hash, then ignore that.
|
63
60
|
result_hash =
|
64
|
-
if object.
|
65
|
-
object.
|
61
|
+
if object.is_a?(Hash)
|
62
|
+
object.merge(result)
|
66
63
|
else
|
67
64
|
result
|
68
65
|
end
|
69
66
|
|
70
67
|
result_hash.keys.each { |k| result_hash.delete(k) if result_hash[k] == Datacaster.absent }
|
71
|
-
Datacaster.ValidResult(result_hash
|
68
|
+
Datacaster.ValidResult(result_hash)
|
72
69
|
else
|
73
70
|
Datacaster.ErrorResult(errors)
|
74
71
|
end
|
@@ -1,43 +1,42 @@
|
|
1
1
|
module Datacaster
|
2
2
|
class HashSchema < Base
|
3
|
-
def initialize(fields)
|
3
|
+
def initialize(fields, error_key = nil)
|
4
4
|
@fields = fields
|
5
|
-
|
6
|
-
@
|
5
|
+
|
6
|
+
@error_keys = ['.hash_value', 'datacaster.errors.hash_value']
|
7
|
+
@error_keys.unshift(error_key) if error_key
|
7
8
|
end
|
8
9
|
|
9
|
-
def cast(object)
|
10
|
-
object
|
11
|
-
return Datacaster.ErrorResult(["must be hash"]) unless object.value.is_a?(Hash)
|
10
|
+
def cast(object, runtime:)
|
11
|
+
return Datacaster.ErrorResult(I18nValues::Key.new(@error_keys, value: object)) unless object.is_a?(Hash)
|
12
12
|
|
13
|
-
|
13
|
+
runtime.will_check!
|
14
14
|
|
15
15
|
errors = {}
|
16
16
|
result = {}
|
17
17
|
|
18
18
|
@fields.each do |key, validator|
|
19
19
|
value =
|
20
|
-
if object.
|
21
|
-
object
|
20
|
+
if object.key?(key)
|
21
|
+
object[key]
|
22
22
|
else
|
23
23
|
Datacaster.absent
|
24
24
|
end
|
25
25
|
|
26
|
-
new_value = validator.(value)
|
26
|
+
new_value = runtime.checked_key!(key) { validator.with_runtime(runtime).(value) }
|
27
27
|
if new_value.valid?
|
28
28
|
result[key] = new_value.value
|
29
|
-
checked_schema[key] = new_value.meta[:checked_schema].dup || true
|
30
29
|
else
|
31
|
-
errors[key] = new_value.
|
30
|
+
errors[key] = new_value.raw_errors
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
34
|
if errors.empty?
|
36
|
-
# All unchecked key-value pairs are passed through, and eliminated by
|
35
|
+
# All unchecked key-value pairs are passed through, and eliminated by ContextNode
|
37
36
|
# at the end of the chain
|
38
|
-
result_hash = object.
|
37
|
+
result_hash = object.merge(result)
|
39
38
|
result_hash.keys.each { |k| result_hash.delete(k) if result_hash[k] == Datacaster.absent }
|
40
|
-
Datacaster.ValidResult(result_hash
|
39
|
+
Datacaster.ValidResult(result_hash)
|
41
40
|
else
|
42
41
|
Datacaster.ErrorResult(errors)
|
43
42
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Datacaster
|
2
|
+
module I18nValues
|
3
|
+
class Base
|
4
|
+
attr_reader :args
|
5
|
+
|
6
|
+
def *(other, additional_scope = [])
|
7
|
+
# To allow redefine array/hash errors with #i18n_key
|
8
|
+
other = other.first if other.is_a?(Array) && other.length == 1
|
9
|
+
result = apply(other, additional_scope)
|
10
|
+
|
11
|
+
result = [result] unless result.is_a?(Array) || result.is_a?(Hash)
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
self.class == other.class && @args == other.args
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def apply(other, additional_scope = [])
|
22
|
+
if !other.is_a?(Base)
|
23
|
+
return apply_to_literal(other, additional_scope)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Key(...) * Scope(...) -> error
|
27
|
+
if is_a?(Key) && !other.is_a?(Key)
|
28
|
+
raise RuntimeError.new("Can not apply #{inspect} to #{other.inspect}")
|
29
|
+
end
|
30
|
+
|
31
|
+
merged_args = other.args.merge(@args)
|
32
|
+
|
33
|
+
# Key(...) * Key(...) -> left
|
34
|
+
if is_a?(Key) && other.is_a?(Key)
|
35
|
+
return Key.new(@keys, merged_args)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Scope('x') * Key(['.relative', 'full_path']) = Key(['x.relative', 'full_path'])
|
39
|
+
if is_a?(Scope) && other.is_a?(Key)
|
40
|
+
scoped_keys =
|
41
|
+
other.keys.flat_map do |x|
|
42
|
+
next x if x[0] != '.'
|
43
|
+
|
44
|
+
keys = ["#{@scope}#{x}"]
|
45
|
+
next keys if x.count('.') > 1
|
46
|
+
|
47
|
+
accumulator = ""
|
48
|
+
additional_scope.each do |k|
|
49
|
+
accumulator << ".#{k}"
|
50
|
+
keys.unshift "#{@scope}#{accumulator}#{x}"
|
51
|
+
end
|
52
|
+
|
53
|
+
keys
|
54
|
+
end
|
55
|
+
return Key.new(scoped_keys, merged_args)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Scope(...) * Scope(...) -> error
|
59
|
+
raise RuntimeError.new("Can not apply #{inspect} to #{other.inspect}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_to_literal(other, additional_scope = [])
|
63
|
+
# Base * Other -> Other
|
64
|
+
return other if !other.is_a?(Hash) && !other.is_a?(Array)
|
65
|
+
|
66
|
+
# Key(...) * Array -> Array
|
67
|
+
# Key(...) * Hash -> Hash
|
68
|
+
return other if is_a?(Key)
|
69
|
+
|
70
|
+
# Scope(...) * Array -> map
|
71
|
+
return other.map { |x| self.*(x, additional_scope) } if other.is_a?(Array)
|
72
|
+
|
73
|
+
# Scope(...) * Hash -> map values
|
74
|
+
other.map do |(k, v)|
|
75
|
+
new_value =
|
76
|
+
case k
|
77
|
+
when String, Symbol
|
78
|
+
self.*(v, [*additional_scope, k])
|
79
|
+
else
|
80
|
+
self.*(v, [*additional_scope])
|
81
|
+
end
|
82
|
+
[k, new_value]
|
83
|
+
end.to_h
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Datacaster
|
2
|
+
module I18nValues
|
3
|
+
class Key < Base
|
4
|
+
attr_reader :keys
|
5
|
+
|
6
|
+
def initialize(keys_or_key, args = {})
|
7
|
+
keys = Array(keys_or_key)
|
8
|
+
@keys = keys
|
9
|
+
@args = args
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
super && @keys == other.keys
|
14
|
+
end
|
15
|
+
|
16
|
+
def resolve
|
17
|
+
keys = @keys.select { |x| x[0] != '.' }
|
18
|
+
if keys.empty?
|
19
|
+
raise RuntimeError.new("No absolute keys among #{@keys.inspect}. Use #i18n_key in addition to #i18n_scope.")
|
20
|
+
end
|
21
|
+
key = keys.find(&Config.i18n_exists?) || keys.first
|
22
|
+
Config.i18n_t.(key, **@args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_args(args)
|
26
|
+
self.class.new(@keys, @args.merge(args))
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#<#{self.class.name}(#{@keys.inspect}) #{@args.inspect}>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Datacaster
|
2
|
+
module I18nValues
|
3
|
+
class Scope < Base
|
4
|
+
attr_reader :scope
|
5
|
+
|
6
|
+
def initialize(scope, args = {})
|
7
|
+
@scope = scope
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
super && @scope == other.scope
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolve
|
16
|
+
raise RuntimeError.new("Tried to resolve i18n scope #{@scope.inspect}. Use #i18n_key in addition to #i18n_scope.")
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_args(args)
|
20
|
+
self.class.new(@scope, @args.merge(args))
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class.name}(#{@scope.inspect}) #{@args.inspect}>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -4,11 +4,8 @@ module Datacaster
|
|
4
4
|
@keys = keys
|
5
5
|
end
|
6
6
|
|
7
|
-
def cast(object)
|
8
|
-
|
9
|
-
object = intermediary_result.value
|
10
|
-
|
11
|
-
return Datacaster.ErrorResult(["must be Hash"]) unless object.is_a?(Hash)
|
7
|
+
def cast(object, runtime:)
|
8
|
+
return Datacaster.ErrorResult(I18nValues::Key.new(['.hash_value', 'datacaster.errors.hash_value'], value: object)) unless object.is_a?(Hash)
|
12
9
|
|
13
10
|
result = set_initial_value(object)
|
14
11
|
|
@@ -35,7 +32,7 @@ module Datacaster
|
|
35
32
|
end
|
36
33
|
|
37
34
|
def need_hash_merger?(object)
|
38
|
-
@need_hash_merger =
|
35
|
+
@need_hash_merger =
|
39
36
|
@need_hash_merger.nil? ? @keys.any? { |k| object[k].is_a?(Hash) } : @need_hash_merger
|
40
37
|
end
|
41
38
|
|
@@ -44,7 +41,7 @@ module Datacaster
|
|
44
41
|
unit = [unit] unless unit.is_a?(Array)
|
45
42
|
|
46
43
|
result = clean(unit | merge_with)
|
47
|
-
result
|
44
|
+
result == [] ? Datacaster.absent : result
|
48
45
|
end
|
49
46
|
|
50
47
|
def value_or(value, default)
|
@@ -54,12 +51,8 @@ module Datacaster
|
|
54
51
|
def merge_hash_with_hash(result, merge_with)
|
55
52
|
if merge_with.is_a?(Hash)
|
56
53
|
merge_with.each do |k, v|
|
57
|
-
|
58
|
-
|
59
|
-
result[k] = merge_hash_with_hash(result[k], v)
|
60
|
-
else
|
61
|
-
result[k] = merge_array_or_scalar(value_or(result[k], []), v)
|
62
|
-
end
|
54
|
+
result = value_or(result, {})
|
55
|
+
result[k] = merge_hash_with_hash(result[k], v)
|
63
56
|
end
|
64
57
|
|
65
58
|
result
|
@@ -85,12 +78,12 @@ module Datacaster
|
|
85
78
|
when Array
|
86
79
|
value.delete_if do |v|
|
87
80
|
clean(v) if v.is_a?(Hash) || v.is_a?(Array)
|
88
|
-
v == Datacaster.absent
|
81
|
+
v == Datacaster.absent || v == nil
|
89
82
|
end
|
90
83
|
when Hash
|
91
84
|
value.delete_if do |_k, v|
|
92
85
|
clean(v) if v.is_a?(Hash) || v.is_a?(Array)
|
93
|
-
v == Datacaster.absent
|
86
|
+
v == Datacaster.absent || v == nil
|
94
87
|
end
|
95
88
|
end
|
96
89
|
end
|
data/lib/datacaster/or_node.rb
CHANGED
@@ -5,13 +5,12 @@ module Datacaster
|
|
5
5
|
@right = right
|
6
6
|
end
|
7
7
|
|
8
|
-
def cast(object)
|
9
|
-
|
10
|
-
left_result = @left.(object)
|
8
|
+
def cast(object, runtime:)
|
9
|
+
left_result = @left.with_runtime(runtime).(object)
|
11
10
|
|
12
11
|
return left_result if left_result.valid?
|
13
12
|
|
14
|
-
@right.(object)
|
13
|
+
@right.with_runtime(runtime).(object)
|
15
14
|
end
|
16
15
|
|
17
16
|
def inspect
|
@@ -4,73 +4,124 @@ module Datacaster
|
|
4
4
|
|
5
5
|
# Base types
|
6
6
|
|
7
|
-
def cast(
|
8
|
-
Caster.new(
|
7
|
+
def cast(&block)
|
8
|
+
Caster.new(&block)
|
9
9
|
end
|
10
10
|
|
11
|
-
def check(
|
12
|
-
Checker.new(
|
11
|
+
def check(error_key = nil, &block)
|
12
|
+
Checker.new(error_key, &block)
|
13
13
|
end
|
14
14
|
|
15
|
-
def compare(value,
|
16
|
-
Comparator.new(value,
|
15
|
+
def compare(value, error_key = nil)
|
16
|
+
Comparator.new(value, error_key)
|
17
17
|
end
|
18
18
|
|
19
|
-
def transform(
|
20
|
-
Transformer.new(
|
19
|
+
def transform(&block)
|
20
|
+
Transformer.new(&block)
|
21
21
|
end
|
22
22
|
|
23
|
-
def transform_if_present(
|
23
|
+
def transform_if_present(&block)
|
24
24
|
raise 'Expected block' unless block_given?
|
25
25
|
|
26
|
-
Transformer.new
|
26
|
+
Transformer.new { |v| v == Datacaster.absent ? v : block.(v) }
|
27
27
|
end
|
28
28
|
|
29
|
-
def try(
|
30
|
-
Trier.new(
|
29
|
+
def try(error_key = nil, catched_exception:, &block)
|
30
|
+
Trier.new(catched_exception, error_key, &block)
|
31
31
|
end
|
32
32
|
|
33
|
-
def array_schema(element_caster)
|
34
|
-
ArraySchema.new(element_caster)
|
33
|
+
def array_schema(element_caster, error_keys = {})
|
34
|
+
ArraySchema.new(DefinitionDSL.expand(element_caster), error_keys)
|
35
35
|
end
|
36
36
|
alias_method :array_of, :array_schema
|
37
37
|
|
38
|
-
def hash_schema(fields)
|
39
|
-
|
38
|
+
def hash_schema(fields, error_key = nil)
|
39
|
+
unless fields.is_a?(Hash)
|
40
|
+
raise "Expected field definitions in a form of Hash for hash_schema, got #{fields.inspect} instead"
|
41
|
+
end
|
42
|
+
HashSchema.new(
|
43
|
+
fields.transform_values { |f| DefinitionDSL.expand(f) },
|
44
|
+
error_key
|
45
|
+
)
|
40
46
|
end
|
41
47
|
|
42
48
|
def transform_to_hash(fields)
|
43
|
-
HashMapper.new(fields)
|
49
|
+
HashMapper.new(fields.transform_values { |x| DefinitionDSL.expand(x) })
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate(active_model_validations)
|
53
|
+
Validator.new(active_model_validations)
|
54
|
+
end
|
55
|
+
|
56
|
+
def schema(base)
|
57
|
+
ContextNodes::StructureCleaner.new(base, strategy: :fail)
|
58
|
+
end
|
59
|
+
|
60
|
+
def choosy_schema(base)
|
61
|
+
ContextNodes::StructureCleaner.new(base, strategy: :remove)
|
44
62
|
end
|
45
63
|
|
46
|
-
def
|
47
|
-
|
64
|
+
def partial_schema(base)
|
65
|
+
ContextNodes::StructureCleaner.new(base, strategy: :pass)
|
48
66
|
end
|
49
67
|
|
50
68
|
# 'Meta' types
|
51
69
|
|
52
|
-
def absent
|
53
|
-
|
70
|
+
def absent(error_key = nil)
|
71
|
+
error_keys = ['.absent', 'datacaster.errors.absent']
|
72
|
+
error_keys.unshift(error_key) if error_key
|
73
|
+
check { |x| x == Datacaster.absent }.i18n_key(*error_keys)
|
54
74
|
end
|
55
75
|
|
56
|
-
def any
|
57
|
-
|
76
|
+
def any(error_key = nil)
|
77
|
+
error_keys = ['.any', 'datacaster.errors.any']
|
78
|
+
error_keys.unshift(error_key) if error_key
|
79
|
+
check { |x| x != Datacaster.absent }.i18n_key(*error_keys)
|
80
|
+
end
|
81
|
+
|
82
|
+
def default(value, on: nil)
|
83
|
+
transform do |x|
|
84
|
+
if x == Datacaster.absent ||
|
85
|
+
(on && x.respond_to?(on) && x.public_send(on))
|
86
|
+
value
|
87
|
+
else
|
88
|
+
x
|
89
|
+
end
|
90
|
+
end
|
58
91
|
end
|
59
92
|
|
60
93
|
def transform_to_value(value)
|
61
|
-
transform
|
94
|
+
transform { value }
|
62
95
|
end
|
63
96
|
|
64
97
|
def remove
|
65
|
-
transform
|
98
|
+
transform { Datacaster.absent }
|
66
99
|
end
|
67
100
|
|
68
101
|
def pass
|
69
|
-
transform(
|
102
|
+
transform(&:itself)
|
103
|
+
end
|
104
|
+
|
105
|
+
def switch(*base, **on_clauses)
|
106
|
+
switch =
|
107
|
+
if base.length == 0
|
108
|
+
SwitchNode.new
|
109
|
+
else
|
110
|
+
SwitchNode.new(base)
|
111
|
+
end
|
112
|
+
on_clauses.reduce(switch) do |result, (k, v)|
|
113
|
+
result.on(k, v)
|
114
|
+
end
|
70
115
|
end
|
71
116
|
|
72
|
-
def
|
73
|
-
|
117
|
+
def pass_if(base)
|
118
|
+
ContextNodes::PassIf.new(base)
|
119
|
+
end
|
120
|
+
|
121
|
+
def pick(*keys, strict: false)
|
122
|
+
raise RuntimeError.new("provide keys to pick, e.g. pick(:key)") if keys.empty?
|
123
|
+
|
124
|
+
must_be(Enumerable) & transform { |value|
|
74
125
|
result =
|
75
126
|
keys.map do |key|
|
76
127
|
if value.respond_to?(:key?) && !value.key?(key)
|
@@ -90,12 +141,16 @@ module Datacaster
|
|
90
141
|
MessageKeysMerger.new(keys)
|
91
142
|
end
|
92
143
|
|
93
|
-
def responds_to(method)
|
94
|
-
|
144
|
+
def responds_to(method, error_key = nil)
|
145
|
+
error_keys = ['.responds_to', 'datacaster.errors.responds_to']
|
146
|
+
error_keys.unshift(error_key) if error_key
|
147
|
+
check { |x| x.respond_to?(method) }.i18n_key(*error_keys, reference: method.to_s)
|
95
148
|
end
|
96
149
|
|
97
|
-
def must_be(klass)
|
98
|
-
|
150
|
+
def must_be(klass, error_key = nil)
|
151
|
+
error_keys = ['.must_be', 'datacaster.errors.must_be']
|
152
|
+
error_keys.unshift(error_key) if error_key
|
153
|
+
check { |x| x.is_a?(klass) }.i18n_key(*error_keys, reference: klass.name)
|
99
154
|
end
|
100
155
|
|
101
156
|
def optional(base)
|
@@ -104,81 +159,111 @@ module Datacaster
|
|
104
159
|
|
105
160
|
# Strict types
|
106
161
|
|
107
|
-
def decimal(digits = 8)
|
108
|
-
|
162
|
+
def decimal(digits = 8, error_key = nil)
|
163
|
+
error_keys = ['.decimal', 'datacaster.errors.decimal']
|
164
|
+
error_keys.unshift(error_key) if error_key
|
165
|
+
|
166
|
+
Trier.new([ArgumentError, TypeError]) do |x|
|
109
167
|
# strictly validate format of string, BigDecimal() doesn't do that
|
110
168
|
Float(x)
|
111
169
|
|
112
170
|
BigDecimal(x, digits)
|
113
|
-
end
|
171
|
+
end.i18n_key(*error_keys)
|
114
172
|
end
|
115
173
|
|
116
|
-
def array
|
117
|
-
|
174
|
+
def array(error_key = nil)
|
175
|
+
error_keys = ['.array', 'datacaster.errors.array']
|
176
|
+
error_keys.unshift(error_key) if error_key
|
177
|
+
check { |x| x.is_a?(Array) }.i18n_key(*error_keys)
|
118
178
|
end
|
119
179
|
|
120
|
-
def float
|
121
|
-
|
180
|
+
def float(error_key = nil)
|
181
|
+
error_keys = ['.float', 'datacaster.errors.float']
|
182
|
+
error_keys.unshift(error_key) if error_key
|
183
|
+
check { |x| x.is_a?(Float) }.i18n_key(*error_keys)
|
122
184
|
end
|
123
185
|
|
124
|
-
# 'hash'
|
125
|
-
def hash_value
|
126
|
-
|
186
|
+
# 'hash' would be a bad method name, because it would override built in Object#hash
|
187
|
+
def hash_value(error_key = nil)
|
188
|
+
error_keys = ['.hash_value', 'datacaster.errors.hash_value']
|
189
|
+
error_keys.unshift(error_key) if error_key
|
190
|
+
check(error_key) { |x| x.is_a?(Hash) }
|
127
191
|
end
|
128
192
|
|
129
|
-
def hash_with_symbolized_keys
|
130
|
-
hash_value & transform
|
193
|
+
def hash_with_symbolized_keys(error_key = nil)
|
194
|
+
hash_value(error_key) & transform { |x| x.symbolize_keys }
|
131
195
|
end
|
132
196
|
|
133
|
-
def integer
|
134
|
-
|
197
|
+
def integer(error_key = nil)
|
198
|
+
error_keys = ['.integer', 'datacaster.errors.integer']
|
199
|
+
error_keys.unshift(error_key) if error_key
|
200
|
+
check { |x| x.is_a?(Integer) }.i18n_key(*error_keys)
|
135
201
|
end
|
136
202
|
|
137
|
-
def integer32
|
138
|
-
|
203
|
+
def integer32(error_key = nil)
|
204
|
+
error_keys = ['.integer32', 'datacaster.errors.integer32']
|
205
|
+
error_keys.unshift(error_key) if error_key
|
206
|
+
integer(error_key) & check { |x| x.abs <= 2_147_483_647 }.i18n_key(*error_keys)
|
139
207
|
end
|
140
208
|
|
141
|
-
def string
|
142
|
-
|
209
|
+
def string(error_key = nil)
|
210
|
+
error_keys = ['.string', 'datacaster.errors.string']
|
211
|
+
error_keys.unshift(error_key) if error_key
|
212
|
+
check { |x| x.is_a?(String) }.i18n_key(*error_keys)
|
143
213
|
end
|
144
214
|
|
145
|
-
def non_empty_string
|
146
|
-
|
215
|
+
def non_empty_string(error_key = nil)
|
216
|
+
error_keys = ['.non_empty_string', 'datacaster.errors.non_empty_string']
|
217
|
+
error_keys.unshift(error_key) if error_key
|
218
|
+
string(error_key) & check { |x| !x.empty? }.i18n_key(*error_keys)
|
147
219
|
end
|
148
220
|
|
149
221
|
# Form request types
|
150
222
|
|
151
|
-
def iso8601
|
152
|
-
|
153
|
-
|
223
|
+
def iso8601(error_key = nil)
|
224
|
+
error_keys = ['.iso8601', 'datacaster.errors.iso8601']
|
225
|
+
error_keys.unshift(error_key) if error_key
|
226
|
+
|
227
|
+
string(error_key) &
|
228
|
+
try(catched_exception: [ArgumentError, TypeError]) { |x| DateTime.iso8601(x) }.
|
229
|
+
i18n_key(*error_keys)
|
154
230
|
end
|
155
231
|
|
156
|
-
def to_boolean
|
157
|
-
|
232
|
+
def to_boolean(error_key = nil)
|
233
|
+
error_keys = ['.to_boolean', 'datacaster.errors.to_boolean']
|
234
|
+
error_keys.unshift(error_key) if error_key
|
235
|
+
|
236
|
+
cast do |x|
|
158
237
|
if ['true', '1', true].include?(x)
|
159
238
|
Datacaster.ValidResult(true)
|
160
239
|
elsif ['false', '0', false].include?(x)
|
161
240
|
Datacaster.ValidResult(false)
|
162
241
|
else
|
163
|
-
Datacaster.ErrorResult(
|
242
|
+
Datacaster.ErrorResult(I18nValues::Key.new(error_keys, value: x))
|
164
243
|
end
|
165
244
|
end
|
166
245
|
end
|
167
246
|
|
168
|
-
def to_float
|
169
|
-
|
247
|
+
def to_float(error_key = nil)
|
248
|
+
error_keys = ['.to_float', 'datacaster.errors.to_float']
|
249
|
+
error_keys.unshift(error_key) if error_key
|
250
|
+
|
251
|
+
Trier.new([ArgumentError, TypeError]) do |x|
|
170
252
|
Float(x)
|
171
|
-
end
|
253
|
+
end.i18n_key(*error_keys)
|
172
254
|
end
|
173
255
|
|
174
|
-
def to_integer
|
175
|
-
|
256
|
+
def to_integer(error_key = nil)
|
257
|
+
error_keys = ['.to_integer', 'datacaster.errors.to_integer']
|
258
|
+
error_keys.unshift(error_key) if error_key
|
259
|
+
|
260
|
+
Trier.new([ArgumentError, TypeError]) do |x|
|
176
261
|
Integer(x)
|
177
|
-
end
|
262
|
+
end.i18n_key(*error_keys)
|
178
263
|
end
|
179
264
|
|
180
265
|
def optional_param(base)
|
181
|
-
transform_if_present
|
266
|
+
transform_if_present { |x| x == '' ? Datacaster::Absent.instance : x } & (absent | base)
|
182
267
|
end
|
183
268
|
end
|
184
269
|
end
|