Module: Datacaster::Predefined

Extended by:
Predefined
Included in:
DefinitionDSL, Predefined
Defined in:
lib/datacaster/predefined.rb

Instance Method Summary collapse

Instance Method Details

#absent(error_key = nil, on: nil) ⇒ Object

‘Meta’ types



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/datacaster/predefined.rb', line 88

def absent(error_key = nil, on: nil)
  error_keys = ['.absent', 'datacaster.errors.absent']
  error_keys.unshift(error_key) if error_key

  cast do |x|
    if x == Datacaster.absent ||
      (!on.nil? && x.respond_to?(on) && x.public_send(on))
      Datacaster.ValidResult(Datacaster.absent)
    else
      Datacaster.ErrorResult(
        I18nValues::Key.new(error_keys, value: x)
      )
    end
  end
end

#any(error_key = nil) ⇒ Object



104
105
106
107
108
# File 'lib/datacaster/predefined.rb', line 104

def any(error_key = nil)
  error_keys = ['.any', 'datacaster.errors.any']
  error_keys.unshift(error_key) if error_key
  check { |x| x != Datacaster.absent }.i18n_key(*error_keys)
end

#array(error_key = nil) ⇒ Object



311
312
313
314
315
# File 'lib/datacaster/predefined.rb', line 311

def array(error_key = nil)
  error_keys = ['.array', 'datacaster.errors.array']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(Array) }.i18n_key(*error_keys)
end

#array_schema(element_caster, error_keys = {}) ⇒ Object Also known as: array_of



37
38
39
# File 'lib/datacaster/predefined.rb', line 37

def array_schema(element_caster, error_keys = {})
  ArraySchema.new(DefinitionDSL.expand(element_caster), error_keys)
end

#attribute(*keys) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/datacaster/predefined.rb', line 110

def attribute(*keys)
  if keys.empty? || keys.any? { |k| !Datacaster::Utils.pickable?(k) }
    raise RuntimeError, "each argument should be String, Symbol, Integer or an array thereof", caller
  end

  transform do |input|
    result =
      keys.map do |key|
        Array(key).reduce(input) do |result, k|
          if result.respond_to?(k)
            result.public_send(k)
          else
            break Datacaster.absent
          end
        end
      end
    keys.length == 1 ? result.first : result
  end
end

#cast(&block) ⇒ Object

Base types



7
8
9
# File 'lib/datacaster/predefined.rb', line 7

def cast(&block)
  Caster.new(&block)
end

#cast_around(&block) ⇒ Object

‘Around’ types



82
83
84
# File 'lib/datacaster/predefined.rb', line 82

def cast_around(&block)
  AroundNodes::Caster.new(&block)
end

#check(error_key = nil, &block) ⇒ Object



11
12
13
# File 'lib/datacaster/predefined.rb', line 11

def check(error_key = nil, &block)
  Checker.new(error_key, &block)
end

#choosy_hash_schema(fields, error_key = nil) ⇒ Object



56
57
58
# File 'lib/datacaster/predefined.rb', line 56

def choosy_hash_schema(fields, error_key = nil)
  choosy_schema(hash_schema(fields, error_key))
end

#choosy_schema(base) ⇒ Object



72
73
74
# File 'lib/datacaster/predefined.rb', line 72

def choosy_schema(base)
  ContextNodes::StructureCleaner.new(base, :remove)
end

#compare(value, error_key = nil) ⇒ Object



15
16
17
# File 'lib/datacaster/predefined.rb', line 15

def compare(value, error_key = nil)
  Comparator.new(value, error_key)
end

#decimal(digits = 8, error_key = nil) ⇒ Object

Strict types



299
300
301
302
303
304
305
306
307
308
309
# File 'lib/datacaster/predefined.rb', line 299

def decimal(digits = 8, error_key = nil)
  error_keys = ['.decimal', 'datacaster.errors.decimal']
  error_keys.unshift(error_key) if error_key

  Trier.new([ArgumentError, TypeError]) do |x|
    # strictly validate format of string, BigDecimal() doesn't do that
    Float(x)

    BigDecimal(x, digits)
  end.i18n_key(*error_keys)
end

#default(value, on: nil) ⇒ Object



130
131
132
133
134
135
136
137
138
139
# File 'lib/datacaster/predefined.rb', line 130

def default(value, on: nil)
  transform do |x|
    if x == Datacaster.absent ||
      (!on.nil? && x.respond_to?(on) && x.public_send(on))
      Datacaster::Utils.deep_freeze(value)
    else
      x
    end
  end
end

#float(error_key = nil) ⇒ Object



317
318
319
320
321
# File 'lib/datacaster/predefined.rb', line 317

def float(error_key = nil)
  error_keys = ['.float', 'datacaster.errors.float']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(Float) }.i18n_key(*error_keys)
end

#hash_schema(fields, error_key = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
# File 'lib/datacaster/predefined.rb', line 42

def hash_schema(fields, error_key = nil)
  unless fields.is_a?(Hash)
    raise "Expected field definitions in a form of Hash for hash_schema, got #{fields.inspect} instead"
  end
  HashSchema.new(
    fields.transform_values { |f| DefinitionDSL.expand(f) },
    error_key
  )
end

#hash_value(error_key = nil) ⇒ Object

‘hash’ would be a bad method name, because it would override built in Object#hash



330
331
332
333
334
# File 'lib/datacaster/predefined.rb', line 330

def hash_value(error_key = nil)
  error_keys = ['.hash_value', 'datacaster.errors.hash_value']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(Hash) }.i18n_key(*error_keys)
end

#hash_with_symbolized_keys(error_key = nil) ⇒ Object



336
337
338
# File 'lib/datacaster/predefined.rb', line 336

def hash_with_symbolized_keys(error_key = nil)
  hash_value(error_key) & transform { |x| x.symbolize_keys }
end

#included_in(values, error_key: nil) ⇒ Object



340
341
342
343
344
# File 'lib/datacaster/predefined.rb', line 340

def included_in(values, error_key: nil)
  error_keys = ['.included_in', 'datacaster.errors.included_in']
  error_keys.unshift(error_key) if error_key
  check { |x| values.include?(x) }.i18n_key(*error_keys, reference: values.map(&:to_s).join(', '))
end

#integer(error_key = nil) ⇒ Object



346
347
348
349
350
# File 'lib/datacaster/predefined.rb', line 346

def integer(error_key = nil)
  error_keys = ['.integer', 'datacaster.errors.integer']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(Integer) }.i18n_key(*error_keys)
end

#integer32(error_key = nil) ⇒ Object



352
353
354
355
356
# File 'lib/datacaster/predefined.rb', line 352

def integer32(error_key = nil)
  error_keys = ['.integer32', 'datacaster.errors.integer32']
  error_keys.unshift(error_key) if error_key
  integer(error_key) & check { |x| x.abs <= 2_147_483_647 }.i18n_key(*error_keys)
end

#iso8601(error_key = nil) ⇒ Object

Form request types



378
379
380
381
382
383
384
385
# File 'lib/datacaster/predefined.rb', line 378

def iso8601(error_key = nil)
  error_keys = ['.iso8601', 'datacaster.errors.iso8601']
  error_keys.unshift(error_key) if error_key

  string(error_key) &
    try(catched_exception: [ArgumentError, TypeError]) { |x| DateTime.iso8601(x) }.
      i18n_key(*error_keys)
end

#merge_message_keys(*keys) ⇒ Object



141
142
143
# File 'lib/datacaster/predefined.rb', line 141

def merge_message_keys(*keys)
  MessageKeysMerger.new(keys)
end

#must_be(klass, error_key = nil) ⇒ Object



145
146
147
148
149
# File 'lib/datacaster/predefined.rb', line 145

def must_be(klass, error_key = nil)
  error_keys = ['.must_be', 'datacaster.errors.must_be']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(klass) }.i18n_key(*error_keys, reference: klass.name)
end

#non_empty_string(error_key = nil) ⇒ Object



364
365
366
367
368
# File 'lib/datacaster/predefined.rb', line 364

def non_empty_string(error_key = nil)
  error_keys = ['.non_empty_string', 'datacaster.errors.non_empty_string']
  error_keys.unshift(error_key) if error_key
  string(error_key) & check { |x| !x.empty? }.i18n_key(*error_keys)
end

#optional(base, on: nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/datacaster/predefined.rb', line 151

def optional(base, on: nil)
  return absent | base if on == nil
  cast do |x|
    if x == Datacaster.absent ||
      (!on.nil? && x.respond_to?(on) && x.public_send(on))
      Datacaster.ValidResult(Datacaster.absent)
    else
      base.(x)
    end
  end
end

#optional_param(base) ⇒ Object



420
421
422
# File 'lib/datacaster/predefined.rb', line 420

def optional_param(base)
  transform_if_present { |x| x == '' ? Datacaster::Absent.instance : x } & (absent | base)
end

#partial_schema(base) ⇒ Object



76
77
78
# File 'lib/datacaster/predefined.rb', line 76

def partial_schema(base)
  ContextNodes::StructureCleaner.new(base, :pass)
end

#passObject



163
164
165
# File 'lib/datacaster/predefined.rb', line 163

def pass
  cast { |v| Datacaster::ValidResult(v) }
end

#pass_if(base) ⇒ Object



167
168
169
# File 'lib/datacaster/predefined.rb', line 167

def pass_if(base)
  ContextNodes::PassIf.new(base)
end

#pattern(regexp, error_key = nil) ⇒ Object



323
324
325
326
327
# File 'lib/datacaster/predefined.rb', line 323

def pattern(regexp, error_key = nil)
  error_keys = ['.pattern', 'datacaster.errors.pattern']
  error_keys.unshift(error_key) if error_key
  string(error_key) & check { |x| x.match?(regexp) }.i18n_key(*error_keys, reference: regexp.inspect)
end

#pick(*keys) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/datacaster/predefined.rb', line 171

def pick(*keys)
  if keys.empty?
    raise RuntimeError, "pick(key, ...) accepts at least one argument", caller
  end

  if wrong = keys.find { |k| !Datacaster::Utils.pickable?(k) }
    raise RuntimeError, "each argument should be String, Symbol, Integer or an array thereof, instead got #{wrong.inspect}", caller
  end

  retrieve_key = -> (from, key) do
    if from.respond_to?(:key?) && !from.key?(key)
      Datacaster.absent
    elsif from.respond_to?(:length) && key.is_a?(Integer) && key > 0 && key >= from.length
      Datacaster.absent
    elsif !from.respond_to?(:[])
      Datacaster.absent
    else
      from[key]
    end
  end

  must_be(Enumerable) & cast { |input|
    result =
      keys.map do |key|
        Array(key).reduce(input) do |result, k|
          result = retrieve_key.(result, k)
          break result if result == Datacaster.absent
          result
        end
      end
    result = keys.length == 1 ? result.first : result
    Datacaster::ValidResult(result)
  }
end

#relate(left, op, right, error_key: nil) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/datacaster/predefined.rb', line 206

def relate(left, op, right, error_key: nil)
  error_keys = ['.relate', 'datacaster.errors.relate']
  additional_vars = {}

  left_caster = left
  if Datacaster::Utils.pickable?(left)
    left_caster = pick(left)
  elsif !Datacaster.instance?(left)
    raise RuntimeError, "provide String, Symbol, Integer or array thereof instead of #{left.inspect}", caller
  end

  right_caster = right
  if Datacaster::Utils.pickable?(right)
    right_caster = pick(right)
  elsif !Datacaster.instance?(right)
    raise RuntimeError, "provide String, Symbol, Integer or array thereof instead of #{right.inspect}", caller
  end

  op_caster = op
  if op.is_a?(String) || op.is_a?(Symbol)
    op_caster = check { |(l, r)| l.respond_to?(op) && l.public_send(op, r) }
  elsif !Datacaster.instance?(left)
    raise RuntimeError, "provide String or Symbol instead of #{op.inspect}", caller
  end

  {left: left, op: op, right: right}.each do |k, v|
    if [String, Symbol, Integer].any? { |c| v.is_a?(c) }
      additional_vars[k] = v
    end
  end

  if additional_vars.length == 3
    error_keys.unshift(".#{left}.#{op}.#{right}")
  end
  error_keys.unshift(error_key) if error_key

  cast do |value|
    left_result = left_caster.(value)
    next left_result unless left_result.valid?
    i18n_var!(:left, left_result.value) unless additional_vars.key?(:left)

    right_result = right_caster.(value)
    next right_result unless right_result.valid?
    i18n_var!(:right, right_result.value) unless additional_vars.key?(:right)

    result = op_caster.([left_result.value, right_result.value])
    next Datacaster.ErrorResult([I18nValues::Key.new(error_keys)]) unless result.valid?

    Datacaster.ValidResult(value)
  end.i18n_vars(additional_vars)
end

#removeObject



258
259
260
# File 'lib/datacaster/predefined.rb', line 258

def remove
  transform { Datacaster.absent }
end

#responds_to(method, error_key = nil) ⇒ Object



262
263
264
265
266
# File 'lib/datacaster/predefined.rb', line 262

def responds_to(method, error_key = nil)
  error_keys = ['.responds_to', 'datacaster.errors.responds_to']
  error_keys.unshift(error_key) if error_key
  check { |x| x.respond_to?(method) }.i18n_key(*error_keys, reference: method.to_s)
end

#run(&block) ⇒ Object



19
20
21
# File 'lib/datacaster/predefined.rb', line 19

def run(&block)
  Runner.new(&block)
end

#schema(base) ⇒ Object



68
69
70
# File 'lib/datacaster/predefined.rb', line 68

def schema(base)
  ContextNodes::StructureCleaner.new(base, :fail)
end

#steps(*casters) ⇒ Object



268
269
270
# File 'lib/datacaster/predefined.rb', line 268

def steps(*casters)
  AndNode.new(*casters)
end

#strict_hash_schema(fields, error_key = nil) ⇒ Object



52
53
54
# File 'lib/datacaster/predefined.rb', line 52

def strict_hash_schema(fields, error_key = nil)
  schema(hash_schema(fields, error_key))
end

#string(error_key = nil) ⇒ Object



358
359
360
361
362
# File 'lib/datacaster/predefined.rb', line 358

def string(error_key = nil)
  error_keys = ['.string', 'datacaster.errors.string']
  error_keys.unshift(error_key) if error_key
  check { |x| x.is_a?(String) }.i18n_key(*error_keys)
end

#switch(base = nil, **on_clauses) ⇒ Object



272
273
274
275
276
277
# File 'lib/datacaster/predefined.rb', line 272

def switch(base = nil, **on_clauses)
  switch = SwitchNode.new(base)
  on_clauses.reduce(switch) do |result, (k, v)|
    result.on(k, v)
  end
end

#to_boolean(error_key = nil) ⇒ Object



387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/datacaster/predefined.rb', line 387

def to_boolean(error_key = nil)
  error_keys = ['.to_boolean', 'datacaster.errors.to_boolean']
  error_keys.unshift(error_key) if error_key

  cast do |x|
    if ['true', '1', true].include?(x)
      Datacaster.ValidResult(true)
    elsif ['false', '0', false].include?(x)
      Datacaster.ValidResult(false)
    else
      Datacaster.ErrorResult(I18nValues::Key.new(error_keys, value: x))
    end
  end
end

#to_float(error_key = nil) ⇒ Object



402
403
404
405
406
407
408
409
# File 'lib/datacaster/predefined.rb', line 402

def to_float(error_key = nil)
  error_keys = ['.to_float', 'datacaster.errors.to_float']
  error_keys.unshift(error_key) if error_key

  Trier.new([ArgumentError, TypeError]) do |x|
    Float(x)
  end.i18n_key(*error_keys)
end

#to_integer(error_key = nil) ⇒ Object



411
412
413
414
415
416
417
418
# File 'lib/datacaster/predefined.rb', line 411

def to_integer(error_key = nil)
  error_keys = ['.to_integer', 'datacaster.errors.to_integer']
  error_keys.unshift(error_key) if error_key

  Trier.new([ArgumentError, TypeError]) do |x|
    Integer(x)
  end.i18n_key(*error_keys)
end

#transform(&block) ⇒ Object



23
24
25
# File 'lib/datacaster/predefined.rb', line 23

def transform(&block)
  Transformer.new(&block)
end

#transform_if_present(&block) ⇒ Object



27
28
29
30
31
# File 'lib/datacaster/predefined.rb', line 27

def transform_if_present(&block)
  raise 'Expected block' unless block_given?

  Transformer.new { |v| v == Datacaster.absent ? v : block.(v) }
end

#transform_to_hash(fields) ⇒ Object



60
61
62
# File 'lib/datacaster/predefined.rb', line 60

def transform_to_hash(fields)
  HashMapper.new(fields.transform_values { |x| DefinitionDSL.expand(x) })
end

#transform_to_value(value) ⇒ Object



279
280
281
# File 'lib/datacaster/predefined.rb', line 279

def transform_to_value(value)
  transform { Datacaster::Utils.deep_freeze(value) }
end

#try(error_key = nil, catched_exception:, &block) ⇒ Object



33
34
35
# File 'lib/datacaster/predefined.rb', line 33

def try(error_key = nil, catched_exception:, &block)
  Trier.new(catched_exception, error_key, &block)
end

#uuid(error_key = nil) ⇒ Object



370
371
372
373
374
# File 'lib/datacaster/predefined.rb', line 370

def uuid(error_key = nil)
  error_keys = ['.uuid', 'datacaster.errors.uuid']
  error_keys.unshift(error_key) if error_key
  string(error_key) & pattern(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/).i18n_key(*error_keys)
end

#validate(active_model_validations) ⇒ Object



64
65
66
# File 'lib/datacaster/predefined.rb', line 64

def validate(active_model_validations)
  Validator.new(active_model_validations)
end

#with(keys, caster) ⇒ Object



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/datacaster/predefined.rb', line 283

def with(keys, caster)
  keys = Array(keys)

  unless Datacaster::Utils.pickable?(keys)
    raise RuntimeError, "provide String, Symbol, Integer or an array thereof instead of #{keys.inspect}", caller
  end

  if keys.length == 1
    return transform_to_hash(keys[0] => pick(keys[0]) & caster)
  end

  with(keys[0], must_be(Enumerable) & with(keys[1..-1], caster))
end