datacaster 6.0.4 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e12cb58554769d09ba9635c472f6aeafe7383fe4837a525075973f5c8f6b02b
4
- data.tar.gz: a6cdaafccde0affef8c15a25cda41145f8b62ab8d1ef9769a3eb664ac10ce7be
3
+ metadata.gz: 01b7b515fe0b568974d1278356098f73459b80550132061b33cd87aec5d19551
4
+ data.tar.gz: e2d08e1e016df1221b40fcafe0fc6b47f430c0a899c8d1f1519e9e06157070fe
5
5
  SHA512:
6
- metadata.gz: b0b721aa63f9e33cb16a9ad4231a79b418b26c2ac480f57a3f4d2561e0795e4912dd373cf9105731d0909b4987152b28a8e4fef3a436676daf8a4e8eb7374f75
7
- data.tar.gz: b1a56fceb24ae23aa2ee05924ca18f9bacb0327cf05f070a9468bef86cce6bb722183cd80800859ecbdc2b0d6d18795265f034eed955308c403a2d8b5bf93cfd
6
+ metadata.gz: ee0e18d9b8a2ac16de10dee56785bbcb53a014e0b6480a5f36c48cdff23694061de7d80ab6cc56090f9e435c65efb4b91086fd9e84f7fc6a3dd7d62c750a9a83
7
+ data.tar.gz: 64c6f333f085f4933e8b093e8feb66a964d4cf4a2797e65ae236c31e618f7aa86becb3be5b99791928161f6b9f7e0e19124ca8ef03bdcc501fb0b7320c5a68dd
@@ -5,9 +5,9 @@ module Datacaster
5
5
  end
6
6
 
7
7
  def cast(object, runtime:)
8
- @runtime = create_runtime(runtime)
9
- result = @base.with_runtime(@runtime).call(object)
10
- transform_result(result)
8
+ runtime = create_runtime(runtime)
9
+ result = @base.with_runtime(runtime).call(object)
10
+ transform_result(result, runtime)
11
11
  end
12
12
 
13
13
  def to_json_schema
@@ -28,23 +28,19 @@ module Datacaster
28
28
  parent
29
29
  end
30
30
 
31
- def runtime
32
- @runtime
33
- end
34
-
35
- def transform_result(result)
31
+ def transform_result(result, runtime)
36
32
  if result.valid?
37
- Datacaster.ValidResult(transform_success(result.value))
33
+ Datacaster.ValidResult(transform_success(result.value, runtime))
38
34
  else
39
- Datacaster.ErrorResult(transform_errors(result.raw_errors))
35
+ Datacaster.ErrorResult(transform_errors(result.raw_errors, runtime))
40
36
  end
41
37
  end
42
38
 
43
- def transform_success(value)
39
+ def transform_success(value, _runtime)
44
40
  value
45
41
  end
46
42
 
47
- def transform_errors(errors)
43
+ def transform_errors(errors, _runtime)
48
44
  errors
49
45
  end
50
46
  end
@@ -8,8 +8,8 @@ module Datacaster
8
8
 
9
9
  private
10
10
 
11
- def transform_errors(errors)
12
- result = @caster.with_runtime(@runtime).(errors)
11
+ def transform_errors(errors, runtime)
12
+ result = @caster.with_runtime(runtime).(errors)
13
13
  if result.valid?
14
14
  result.value
15
15
  else
@@ -12,7 +12,7 @@ module Datacaster
12
12
  Runtimes::I18n.new(parent)
13
13
  end
14
14
 
15
- def transform_errors(errors)
15
+ def transform_errors(errors, runtime)
16
16
  @i18n_value.with_args(runtime.args) * errors
17
17
  end
18
18
  end
@@ -9,7 +9,7 @@ module Datacaster
9
9
 
10
10
  private
11
11
 
12
- def transform_errors(errors)
12
+ def transform_errors(errors, _runtime)
13
13
  return errors unless errors.length == 1 && errors.is_a?(Array)
14
14
 
15
15
  error = errors.first
@@ -2,8 +2,7 @@ module Datacaster
2
2
  module ContextNodes
3
3
  class PassIf < Datacaster::ContextNode
4
4
  def cast(object, runtime:)
5
- @runtime = create_runtime(runtime)
6
- result = @base.with_runtime(@runtime).call(object)
5
+ result = @base.with_runtime(create_runtime(runtime)).call(object)
7
6
  result.valid? ? Datacaster::ValidResult(object) : result
8
7
  end
9
8
  end
@@ -21,26 +21,26 @@ module Datacaster
21
21
  Runtimes::StructureCleaner.new(parent)
22
22
  end
23
23
 
24
- def transform_result(result)
24
+ def transform_result(result, runtime)
25
25
  return result unless result.valid?
26
- result = cast_success(result)
26
+ result = cast_success(result, runtime)
27
27
 
28
28
  # Notify parent runtime of current runtime's schema
29
- unless @runtime.unchecked?
30
- parent_runtime = @runtime.instance_variable_get(:@parent)
29
+ unless runtime.unchecked?
30
+ parent_runtime = runtime.instance_variable_get(:@parent)
31
31
  if parent_runtime.respond_to?(:checked_schema!)
32
- parent_runtime.checked_schema!(@runtime.checked_schema)
32
+ parent_runtime.checked_schema!(runtime.checked_schema)
33
33
  end
34
34
  end
35
35
 
36
36
  result
37
37
  end
38
38
 
39
- def cast_success(result)
39
+ def cast_success(result, runtime)
40
40
  return result if @strategy == :pass
41
- return result if @runtime.unchecked?
41
+ return result if runtime.unchecked?
42
42
 
43
- cast_value(result, @runtime.checked_schema)
43
+ cast_value(result, runtime.checked_schema)
44
44
  end
45
45
 
46
46
  def cast_value(result, schema)
@@ -7,11 +7,12 @@ module Datacaster
7
7
  def cast(object, runtime:)
8
8
  return Datacaster.ErrorResult(I18nValues::Key.new(['.hash_value', 'datacaster.errors.hash_value'], value: object)) unless object.is_a?(Hash)
9
9
 
10
- result = set_initial_value(object)
10
+ hash_merger = @keys.any? { |k| object[k].is_a?(Hash) }
11
+ result = hash_merger ? {} : []
11
12
 
12
13
  @keys.each do |k|
13
14
  result =
14
- if need_hash_merger?(object)
15
+ if hash_merger
15
16
  merge_hash(result, object[k])
16
17
  else
17
18
  merge_array_or_scalar(result, object[k])
@@ -27,15 +28,6 @@ module Datacaster
27
28
 
28
29
  private
29
30
 
30
- def set_initial_value(object)
31
- need_hash_merger?(object) ? {} : []
32
- end
33
-
34
- def need_hash_merger?(object)
35
- @need_hash_merger =
36
- @need_hash_merger.nil? ? @keys.any? { |k| object[k].is_a?(Hash) } : @need_hash_merger
37
- end
38
-
39
31
  def merge_array_or_scalar(unit, merge_with)
40
32
  merge_with = [merge_with] unless merge_with.is_a?(Array)
41
33
  unit = [unit] unless unit.is_a?(Array)
@@ -6,7 +6,7 @@ module Datacaster
6
6
  end
7
7
 
8
8
  def cast(object, runtime:)
9
- left_result = @left.with_runtime(runtime).(object)
9
+ left_result = runtime.record_checks_on_success { @left.with_runtime(runtime).(object) }
10
10
 
11
11
  return left_result if left_result.valid?
12
12
 
@@ -171,7 +171,7 @@ module Datacaster
171
171
  base
172
172
 
173
173
  caster
174
- .json_schema(base.to_json_schema)
174
+ .json_schema { base.to_json_schema }
175
175
  .json_schema_attributes(required: false)
176
176
  end
177
177
 
@@ -342,6 +342,11 @@ module Datacaster
342
342
  # strictly validate format of string, BigDecimal() doesn't do that
343
343
  Float(x)
344
344
 
345
+ # BigDecimal(Float, digits) keeps `digits` significant digits, so
346
+ # BigDecimal(1234567.89, 8) is 1234567.9. The Float's own string
347
+ # form parses exactly: BigDecimal("1234567.89") is 1234567.89.
348
+ x = x.to_s if x.is_a?(Float)
349
+
345
350
  BigDecimal(x, digits)
346
351
  end.i18n_key(*error_keys).json_schema(type: 'string')
347
352
  end
@@ -38,6 +38,10 @@ module Datacaster
38
38
  !@parent.nil? && @parent.respond_to?(m, include_private)
39
39
  end
40
40
 
41
+ def record_checks_on_success(&block)
42
+ @parent ? @parent.record_checks_on_success(&block) : yield
43
+ end
44
+
41
45
  def after_call!(sender)
42
46
  @parent.after_call!(sender) if @parent
43
47
  end
@@ -21,8 +21,9 @@ module Datacaster
21
21
  return
22
22
  end
23
23
 
24
- @pointer_stack.last[key] ||= {}
25
- @pointer_stack.push(@pointer_stack.last[key])
24
+ entry = (@pointer_stack.last[key] ||= {})
25
+ # A key checked unconditionally stays so; checks made inside it are dropped
26
+ @pointer_stack.push(entry == true ? {} : entry)
26
27
  @should_check_stack.push(false)
27
28
  result = yield if block_given?
28
29
  was_checked = @should_check_stack.pop
@@ -53,10 +54,33 @@ module Datacaster
53
54
  # Child runtime's schema should be merged with current runtime's schema
54
55
  else
55
56
  will_check!
56
- @pointer_stack[-1].merge!(schema)
57
+ Utils.merge_checks!(@pointer_stack[-1], schema)
57
58
  end
58
59
  end
59
60
 
61
+ # Records the checks made by the block only if its result is valid,
62
+ # as a nested schema reports its checks to the parent only on success
63
+ def record_checks_on_success
64
+ level = @pointer_stack[-1]
65
+ saved_should_check = @should_check_stack[-1]
66
+ @pointer_stack[-1] = {}
67
+
68
+ result = yield
69
+ branch = @pointer_stack[-1]
70
+ @pointer_stack[-1] = level
71
+
72
+ if result.valid?
73
+ if branch == true
74
+ @pointer_stack[-1] = true unless level == true
75
+ elsif level != true
76
+ Utils.merge_checks!(level, branch)
77
+ end
78
+ else
79
+ @should_check_stack[-1] = saved_should_check
80
+ end
81
+ result
82
+ end
83
+
60
84
  def ignore_checks!(&block)
61
85
  @ignore = true
62
86
  result = yield
@@ -63,7 +63,7 @@ module Datacaster
63
63
  end
64
64
 
65
65
  @ons.each do |check, clause|
66
- result = check.with_runtime(runtime).(switch_result)
66
+ result = runtime.record_checks_on_success { check.with_runtime(runtime).(switch_result) }
67
67
  next unless result.valid?
68
68
 
69
69
  runtime.checked_key!(@pick_key) if !@pick_key.nil? && !@pick_key.is_a?(Array)
@@ -17,7 +17,7 @@ module Datacaster
17
17
  raise ArgumentError.new('Datacaster: use "a & b" instead of "a.then(b)" when there is no else-clause')
18
18
  end
19
19
 
20
- left_result = @left.with_runtime(runtime).(object)
20
+ left_result = runtime.record_checks_on_success { @left.with_runtime(runtime).(object) }
21
21
 
22
22
  if left_result.valid?
23
23
  @then.with_runtime(runtime).(left_result.value)
@@ -9,6 +9,12 @@ module Datacaster
9
9
  first.merge(second.to_h, &merger)
10
10
  end
11
11
 
12
+ # Merges checked schemas key by key: a key accepted whole (true) on either side
13
+ # stays accepted whole, nested checks on both sides combine
14
+ def merge_checks!(into, from)
15
+ into.merge!(from) { |_, a, b| a == true || b == true ? true : merge_checks!(a, b) }
16
+ end
17
+
12
18
  def deep_freeze(value, copy: true)
13
19
  Ractor.make_shareable(value, copy:)
14
20
  end
@@ -1,3 +1,3 @@
1
1
  module Datacaster
2
- VERSION = "6.0.4"
2
+ VERSION = "6.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datacaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.4
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugene Zolotarev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-17 00:00:00.000000000 Z
11
+ date: 2026-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel