datacaster 2.0.2 → 3.0.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +518 -268
  3. data/config/locales/en.yml +24 -0
  4. data/datacaster.gemspec +2 -0
  5. data/lib/datacaster/absent.rb +4 -0
  6. data/lib/datacaster/and_node.rb +3 -5
  7. data/lib/datacaster/and_with_error_aggregation_node.rb +5 -6
  8. data/lib/datacaster/array_schema.rb +18 -16
  9. data/lib/datacaster/base.rb +33 -44
  10. data/lib/datacaster/caster.rb +4 -8
  11. data/lib/datacaster/checker.rb +8 -10
  12. data/lib/datacaster/comparator.rb +9 -9
  13. data/lib/datacaster/config.rb +28 -0
  14. data/lib/datacaster/context_node.rb +43 -0
  15. data/lib/datacaster/context_nodes/errors_caster.rb +21 -0
  16. data/lib/datacaster/context_nodes/i18n.rb +20 -0
  17. data/lib/datacaster/context_nodes/i18n_keys_mapper.rb +27 -0
  18. data/lib/datacaster/context_nodes/structure_cleaner.rb +103 -0
  19. data/lib/datacaster/context_nodes/user_context.rb +20 -0
  20. data/lib/datacaster/definition_dsl.rb +37 -0
  21. data/lib/datacaster/hash_mapper.rb +13 -16
  22. data/lib/datacaster/hash_schema.rb +14 -15
  23. data/lib/datacaster/i18n_values/base.rb +87 -0
  24. data/lib/datacaster/i18n_values/key.rb +34 -0
  25. data/lib/datacaster/i18n_values/scope.rb +28 -0
  26. data/lib/datacaster/message_keys_merger.rb +8 -15
  27. data/lib/datacaster/or_node.rb +3 -4
  28. data/lib/datacaster/predefined.rb +119 -64
  29. data/lib/datacaster/result.rb +35 -14
  30. data/lib/datacaster/runtimes/base.rb +47 -0
  31. data/lib/datacaster/runtimes/i18n.rb +20 -0
  32. data/lib/datacaster/runtimes/structure_cleaner.rb +47 -0
  33. data/lib/datacaster/runtimes/user_context.rb +39 -0
  34. data/lib/datacaster/substitute_i18n.rb +48 -0
  35. data/lib/datacaster/then_node.rb +7 -8
  36. data/lib/datacaster/transformer.rb +4 -8
  37. data/lib/datacaster/trier.rb +9 -11
  38. data/lib/datacaster/validator.rb +8 -9
  39. data/lib/datacaster/version.rb +1 -1
  40. data/lib/datacaster.rb +15 -35
  41. metadata +57 -9
  42. data/lib/datacaster/definition_context.rb +0 -20
  43. 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.errors
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
- checked_schema[new_key] = true
40
+ runtime.checked_key!(new_key)
44
41
  end
45
42
  else
46
43
  result[key] = new_value.value
47
- checked_schema[key] = true
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.errors).to_h)
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.errors})
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 Terminator
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.value.is_a?(Hash)
65
- object.value.merge(result)
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, meta: {checked_schema: checked_schema})
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
- # support of shortcut nested validation definitions, e.g. array_schema(a: [integer], b: {c: integer})
6
- @fields.transform_values! { |validator| shortcut_definition(validator) }
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 = super(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
- checked_schema = object.meta[:checked_schema].dup || {}
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.value.key?(key)
21
- object.value[key]
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.errors
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 Terminator
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.value.merge(result)
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, meta: {checked_schema: checked_schema})
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
- intermediary_result = super(object)
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.uniq == [nil] || result == [] ? Datacaster.absent : 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
- if merge_with.is_a?(Hash)
58
- result = value_or(result, {})
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
@@ -5,13 +5,12 @@ module Datacaster
5
5
  @right = right
6
6
  end
7
7
 
8
- def cast(object)
9
- object = super(object)
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,94 @@ module Datacaster
4
4
 
5
5
  # Base types
6
6
 
7
- def cast(name = 'Anonymous', &block)
8
- Caster.new(name, &block)
7
+ def cast(&block)
8
+ Caster.new(&block)
9
9
  end
10
10
 
11
- def check(name = 'Anonymous', error = 'is invalid', &block)
12
- Checker.new(name, error, &block)
11
+ def check(error_key = nil, &block)
12
+ Checker.new(error_key, &block)
13
13
  end
14
14
 
15
- def compare(value, name = 'Anonymous', error = nil)
16
- Comparator.new(value, name, error)
15
+ def compare(value, error_key = nil)
16
+ Comparator.new(value, error_key)
17
17
  end
18
18
 
19
- def transform(name = 'Anonymous', &block)
20
- Transformer.new(name, &block)
19
+ def transform(&block)
20
+ Transformer.new(&block)
21
21
  end
22
22
 
23
- def transform_if_present(name = 'Anonymous', &block)
23
+ def transform_if_present(&block)
24
24
  raise 'Expected block' unless block_given?
25
25
 
26
- Transformer.new(name) { |v| v == Datacaster.absent ? v : block.(v) }
26
+ Transformer.new { |v| v == Datacaster.absent ? v : block.(v) }
27
27
  end
28
28
 
29
- def try(name = 'Anonymous', error = 'is invalid', catched_exception:, &block)
30
- Trier.new(name, error, catched_exception, &block)
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
- HashSchema.new(fields)
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) })
44
50
  end
45
51
 
46
- def validate(active_model_validations, name = 'Anonymous')
47
- Validator.new(active_model_validations, name)
52
+ def validate(active_model_validations)
53
+ Validator.new(active_model_validations)
48
54
  end
49
55
 
50
56
  # 'Meta' types
51
57
 
52
- def absent
53
- check('Absent', 'must be absent') { |x| x == Datacaster.absent }
58
+ def absent(error_key = nil)
59
+ error_keys = ['.absent', 'datacaster.errors.absent']
60
+ error_keys.unshift(error_key) if error_key
61
+ check { |x| x == Datacaster.absent }.i18n_key(*error_keys)
54
62
  end
55
63
 
56
- def any
57
- check('Any', 'must be set') { |x| x != Datacaster.absent }
64
+ def any(error_key = nil)
65
+ error_keys = ['.any', 'datacaster.errors.any']
66
+ error_keys.unshift(error_key) if error_key
67
+ check { |x| x != Datacaster.absent }.i18n_key(*error_keys)
68
+ end
69
+
70
+ def default(value, on: nil)
71
+ transform do |x|
72
+ if x == Datacaster.absent ||
73
+ (on && x.respond_to?(on) && x.public_send(on))
74
+ value
75
+ else
76
+ x
77
+ end
78
+ end
58
79
  end
59
80
 
60
81
  def transform_to_value(value)
61
- transform('ToValue') { value }
82
+ transform { value }
62
83
  end
63
84
 
64
85
  def remove
65
- transform('Remove') { Datacaster.absent }
86
+ transform { Datacaster.absent }
66
87
  end
67
88
 
68
89
  def pass
69
- transform('Pass', &:itself)
90
+ transform(&:itself)
70
91
  end
71
92
 
72
93
  def pick(*keys)
73
- must_be(Enumerable) & transform("Picker") { |value|
94
+ must_be(Enumerable) & transform { |value|
74
95
  result =
75
96
  keys.map do |key|
76
97
  if value.respond_to?(:key?) && !value.key?(key)
@@ -90,12 +111,16 @@ module Datacaster
90
111
  MessageKeysMerger.new(keys)
91
112
  end
92
113
 
93
- def responds_to(method)
94
- check('RespondsTo', "must respond to #{method.inspect}") { |x| x.respond_to?(method) }
114
+ def responds_to(method, error_key = nil)
115
+ error_keys = ['.responds_to', 'datacaster.errors.responds_to']
116
+ error_keys.unshift(error_key) if error_key
117
+ check { |x| x.respond_to?(method) }.i18n_key(*error_keys, reference: method.to_s)
95
118
  end
96
119
 
97
- def must_be(klass)
98
- check('MustBe', "must be #{klass.inspect}") { |x| x.is_a?(klass) }
120
+ def must_be(klass, error_key = nil)
121
+ error_keys = ['.must_be', 'datacaster.errors.must_be']
122
+ error_keys.unshift(error_key) if error_key
123
+ check { |x| x.is_a?(klass) }.i18n_key(*error_keys, reference: klass.name)
99
124
  end
100
125
 
101
126
  def optional(base)
@@ -104,81 +129,111 @@ module Datacaster
104
129
 
105
130
  # Strict types
106
131
 
107
- def decimal(digits = 8)
108
- Trier.new('Decimal', 'must be decimal', [ArgumentError, TypeError]) do |x|
132
+ def decimal(digits = 8, error_key = nil)
133
+ error_keys = ['.decimal', 'datacaster.errors.decimal']
134
+ error_keys.unshift(error_key) if error_key
135
+
136
+ Trier.new([ArgumentError, TypeError]) do |x|
109
137
  # strictly validate format of string, BigDecimal() doesn't do that
110
138
  Float(x)
111
139
 
112
140
  BigDecimal(x, digits)
113
- end
141
+ end.i18n_key(*error_keys)
114
142
  end
115
143
 
116
- def array
117
- check('Array', 'must be array') { |x| x.is_a?(Array) }
144
+ def array(error_key = nil)
145
+ error_keys = ['.array', 'datacaster.errors.array']
146
+ error_keys.unshift(error_key) if error_key
147
+ check { |x| x.is_a?(Array) }.i18n_key(*error_keys)
118
148
  end
119
149
 
120
- def float
121
- check('Float', 'must be float') { |x| x.is_a?(Float) }
150
+ def float(error_key = nil)
151
+ error_keys = ['.float', 'datacaster.errors.float']
152
+ error_keys.unshift(error_key) if error_key
153
+ check { |x| x.is_a?(Float) }.i18n_key(*error_keys)
122
154
  end
123
155
 
124
- # 'hash' is a bad method name, because it will overwrite built in Object#hash
125
- def hash_value
126
- check('Hash', 'must be hash') { |x| x.is_a?(Hash) }
156
+ # 'hash' would be a bad method name, because it would override built in Object#hash
157
+ def hash_value(error_key = nil)
158
+ error_keys = ['.hash_value', 'datacaster.errors.hash_value']
159
+ error_keys.unshift(error_key) if error_key
160
+ check(error_key) { |x| x.is_a?(Hash) }
127
161
  end
128
162
 
129
- def hash_with_symbolized_keys
130
- hash_value & transform("SymbolizeKeys") { |x| x.symbolize_keys }
163
+ def hash_with_symbolized_keys(error_key = nil)
164
+ hash_value(error_key) & transform { |x| x.symbolize_keys }
131
165
  end
132
166
 
133
- def integer
134
- check('Integer', 'must be integer') { |x| x.is_a?(Integer) }
167
+ def integer(error_key = nil)
168
+ error_keys = ['.integer', 'datacaster.errors.integer']
169
+ error_keys.unshift(error_key) if error_key
170
+ check { |x| x.is_a?(Integer) }.i18n_key(*error_keys)
135
171
  end
136
172
 
137
- def integer32
138
- integer & check('FourBytes', 'out of range') { |x| x.abs <= 2_147_483_647 }
173
+ def integer32(error_key = nil)
174
+ error_keys = ['.integer32', 'datacaster.errors.integer32']
175
+ error_keys.unshift(error_key) if error_key
176
+ integer(error_key) & check { |x| x.abs <= 2_147_483_647 }.i18n_key(*error_keys)
139
177
  end
140
178
 
141
- def string
142
- check('String', 'must be string') { |x| x.is_a?(String) }
179
+ def string(error_key = nil)
180
+ error_keys = ['.string', 'datacaster.errors.string']
181
+ error_keys.unshift(error_key) if error_key
182
+ check { |x| x.is_a?(String) }.i18n_key(*error_keys)
143
183
  end
144
184
 
145
- def non_empty_string
146
- string & check('NonEmptyString', 'must be present') { |x| !x.empty? }
185
+ def non_empty_string(error_key = nil)
186
+ error_keys = ['.non_empty_string', 'datacaster.errors.non_empty_string']
187
+ error_keys.unshift(error_key) if error_key
188
+ string(error_key) & check { |x| !x.empty? }.i18n_key(*error_keys)
147
189
  end
148
190
 
149
191
  # Form request types
150
192
 
151
- def iso8601
152
- string &
153
- try('ISO8601', 'must be iso8601 string', catched_exception: [ArgumentError, TypeError]) { |x| DateTime.iso8601(x) }
193
+ def iso8601(error_key = nil)
194
+ error_keys = ['.iso8601', 'datacaster.errors.iso8601']
195
+ error_keys.unshift(error_key) if error_key
196
+
197
+ string(error_key) &
198
+ try(catched_exception: [ArgumentError, TypeError]) { |x| DateTime.iso8601(x) }.
199
+ i18n_key(*error_keys)
154
200
  end
155
201
 
156
- def to_boolean
157
- cast('ToBoolean') do |x|
202
+ def to_boolean(error_key = nil)
203
+ error_keys = ['.to_boolean', 'datacaster.errors.to_boolean']
204
+ error_keys.unshift(error_key) if error_key
205
+
206
+ cast do |x|
158
207
  if ['true', '1', true].include?(x)
159
208
  Datacaster.ValidResult(true)
160
209
  elsif ['false', '0', false].include?(x)
161
210
  Datacaster.ValidResult(false)
162
211
  else
163
- Datacaster.ErrorResult(['must be boolean'])
212
+ Datacaster.ErrorResult(Datacaster::I18nValues::Key.new(error_keys, value: x))
164
213
  end
165
214
  end
166
215
  end
167
216
 
168
- def to_float
169
- Trier.new('ToFloat', 'must be float', [ArgumentError, TypeError]) do |x|
217
+ def to_float(error_key = nil)
218
+ error_keys = ['.to_float', 'datacaster.errors.to_float']
219
+ error_keys.unshift(error_key) if error_key
220
+
221
+ Trier.new([ArgumentError, TypeError]) do |x|
170
222
  Float(x)
171
- end
223
+ end.i18n_key(*error_keys)
172
224
  end
173
225
 
174
- def to_integer
175
- Trier.new('ToInteger', 'must be integer', [ArgumentError, TypeError]) do |x|
226
+ def to_integer(error_key = nil)
227
+ error_keys = ['.to_integer', 'datacaster.errors.to_integer']
228
+ error_keys.unshift(error_key) if error_key
229
+
230
+ Trier.new([ArgumentError, TypeError]) do |x|
176
231
  Integer(x)
177
- end
232
+ end.i18n_key(*error_keys)
178
233
  end
179
234
 
180
235
  def optional_param(base)
181
- transform_if_present("optional_param(#{base.inspect})") { |x| x == '' ? Datacaster::Absent.instance : x } & (absent | base)
236
+ transform_if_present { |x| x == '' ? Datacaster::Absent.instance : x } & (absent | base)
182
237
  end
183
238
  end
184
239
  end