datacaster 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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