julewire-core 1.0.0 → 1.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/CHANGELOG.md +29 -0
- data/docs/advanced-configuration.md +2 -1
- data/docs/configuration.md +14 -7
- data/docs/context-and-propagation.md +26 -4
- data/docs/contracts.md +25 -50
- data/docs/extensions-and-api.md +87 -88
- data/docs/health-schema.md +12 -2
- data/docs/internals.md +24 -23
- data/docs/outputs-and-lifecycle.md +15 -8
- data/docs/records-and-data-policy.md +38 -7
- data/lib/julewire/core/cli/doctor.rb +20 -16
- data/lib/julewire/core/cli/line_helpers.rb +23 -12
- data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
- data/lib/julewire/core/cli/log_formats/record_decoder.rb +1 -1
- data/lib/julewire/core/cli/log_formats.rb +9 -9
- data/lib/julewire/core/cli/tail.rb +19 -14
- data/lib/julewire/core/cli/transcode.rb +4 -5
- data/lib/julewire/core/cli.rb +1 -1
- data/lib/julewire/core/configuration.rb +1 -1
- data/lib/julewire/core/context_store.rb +56 -87
- data/lib/julewire/core/destinations/chaos_output.rb +2 -2
- data/lib/julewire/core/destinations/collection.rb +8 -16
- data/lib/julewire/core/destinations/definition.rb +1 -9
- data/lib/julewire/core/destinations/destination.rb +16 -66
- data/lib/julewire/core/destinations/processor_handling.rb +78 -0
- data/lib/julewire/core/destinations/registry.rb +1 -1
- data/lib/julewire/core/destinations/sink.rb +0 -1
- data/lib/julewire/core/destinations/synchronized_output.rb +54 -17
- data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
- data/lib/julewire/core/destinations/write_step.rb +5 -5
- data/lib/julewire/core/destinations.rb +0 -7
- data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
- data/lib/julewire/core/diagnostics/doctor.rb +22 -15
- data/lib/julewire/core/diagnostics/failure_snapshot.rb +9 -3
- data/lib/julewire/core/diagnostics/health.rb +41 -57
- data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
- data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
- data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
- data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
- data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
- data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
- data/lib/julewire/core/diagnostics/tail.rb +18 -22
- data/lib/julewire/core/execution/boundary.rb +2 -8
- data/lib/julewire/core/execution/handle.rb +14 -17
- data/lib/julewire/core/execution/lineage.rb +26 -62
- data/lib/julewire/core/execution/measurement_handle.rb +6 -10
- data/lib/julewire/core/execution/scope.rb +17 -22
- data/lib/julewire/core/execution/scope_fields.rb +4 -13
- data/lib/julewire/core/execution/scope_identity.rb +1 -9
- data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
- data/lib/julewire/core/execution/summary_state.rb +16 -19
- data/lib/julewire/core/execution/view.rb +8 -17
- data/lib/julewire/core/facade_methods.rb +67 -65
- data/lib/julewire/core/fields/attribute_keys.rb +0 -2
- data/lib/julewire/core/fields/bags.rb +23 -27
- data/lib/julewire/core/fields/field_set.rb +52 -16
- data/lib/julewire/core/fields/field_stack.rb +69 -109
- data/lib/julewire/core/fields/internal/deletion.rb +6 -9
- data/lib/julewire/core/fields/internal.rb +35 -26
- data/lib/julewire/core/fields/lookup.rb +9 -13
- data/lib/julewire/core/fields/section_proxy.rb +1 -9
- data/lib/julewire/core/fields/stack_set.rb +9 -14
- data/lib/julewire/core/fields/static_labels.rb +1 -1
- data/lib/julewire/core/integration/configurable.rb +4 -3
- data/lib/julewire/core/integration/destination_health.rb +28 -7
- data/lib/julewire/core/integration/event_subscriber.rb +1 -1
- data/lib/julewire/core/integration/facade.rb +12 -15
- data/lib/julewire/core/integration/fork_hooks.rb +12 -36
- data/lib/julewire/core/integration/health.rb +0 -3
- data/lib/julewire/core/integration/ivar_state.rb +0 -5
- data/lib/julewire/core/integration/protocol.rb +20 -0
- data/lib/julewire/core/integration/settings.rb +4 -4
- data/lib/julewire/core/integration/subscriber_install.rb +2 -2
- data/lib/julewire/core/integration/subscription.rb +3 -2
- data/lib/julewire/core/integration/values.rb +0 -17
- data/lib/julewire/core/local_storage.rb +3 -19
- data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
- data/lib/julewire/core/processing/level_threshold.rb +7 -14
- data/lib/julewire/core/processing/match.rb +6 -4
- data/lib/julewire/core/processing/pipeline.rb +46 -40
- data/lib/julewire/core/processing/processor_chain.rb +22 -11
- data/lib/julewire/core/processing/processor_registry.rb +3 -4
- data/lib/julewire/core/processing/record_field_transform.rb +0 -4
- data/lib/julewire/core/processing/sampling.rb +1 -3
- data/lib/julewire/core/processing.rb +2 -2
- data/lib/julewire/core/propagation/carrier.rb +73 -19
- data/lib/julewire/core/propagation.rb +18 -4
- data/lib/julewire/core/records/build_input.rb +75 -0
- data/lib/julewire/core/records/console_formatter.rb +6 -6
- data/lib/julewire/core/records/deconstruct.rb +1 -1
- data/lib/julewire/core/records/display_message.rb +2 -4
- data/lib/julewire/core/records/draft.rb +127 -258
- data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
- data/lib/julewire/core/records/metadata.rb +4 -3
- data/lib/julewire/core/records/public_projection.rb +2 -2
- data/lib/julewire/core/records/raw_input.rb +1 -1
- data/lib/julewire/core/records/record.rb +30 -18
- data/lib/julewire/core/records/severity.rb +7 -15
- data/lib/julewire/core/runtime.rb +87 -72
- data/lib/julewire/core/runtime_registry.rb +20 -15
- data/lib/julewire/core/runtime_state.rb +5 -3
- data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
- data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
- data/lib/julewire/core/serialization/bounded_transform.rb +3 -2
- data/lib/julewire/core/serialization/bounded_traversal.rb +72 -83
- data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
- data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
- data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
- data/lib/julewire/core/serialization/exception_shape.rb +7 -6
- data/lib/julewire/core/serialization/json_encoder.rb +10 -13
- data/lib/julewire/core/serialization/serializer.rb +22 -49
- data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
- data/lib/julewire/core/serialization/text_encoder.rb +5 -5
- data/lib/julewire/core/serialization/truncation_metadata.rb +157 -0
- data/lib/julewire/core/serialization/value_copy.rb +183 -109
- data/lib/julewire/core/serialization/value_traversal.rb +10 -127
- data/lib/julewire/core/testing.rb +6 -56
- data/lib/julewire/core/validation.rb +2 -3
- data/lib/julewire/core/version.rb +1 -1
- data/lib/julewire/core.rb +2 -7
- metadata +7 -19
- data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
- data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
- data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
- data/lib/julewire/core/testing/chaos/destination.rb +0 -55
- data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
- data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
- data/lib/julewire/core/testing/chaos.rb +0 -80
- data/lib/julewire/core/testing/contracts/component.rb +0 -162
- data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
- data/lib/julewire/core/testing/contracts/integration.rb +0 -166
- data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
- data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
- data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
- data/lib/julewire/core/testing/contracts/wire.rb +0 -60
- data/lib/julewire/core/testing/contracts.rb +0 -24
- data/lib/julewire/core/testing/coverage.rb +0 -58
- data/lib/julewire/core/testing/test_reports.rb +0 -78
|
@@ -2,54 +2,94 @@
|
|
|
2
2
|
|
|
3
3
|
module Julewire
|
|
4
4
|
module Core
|
|
5
|
+
module FacadePrivateMethods
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def punk_banner
|
|
9
|
+
"!!JULEWIRE PUNK!! chaos containment armed\n"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def punk_chaos_output(output, chaos)
|
|
13
|
+
options = chaos if chaos.is_a?(Hash)
|
|
14
|
+
Destinations::ChaosOutput.new(output, **options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def emit_with_severity(severity, record, fields, &)
|
|
18
|
+
if !block_given? && !record.is_a?(Hash)
|
|
19
|
+
# Scalar eager logs stay allocation-light; lazy inputs need the wrapper
|
|
20
|
+
# so block-built records can still receive the eager severity.
|
|
21
|
+
emit(eager_severity_input(severity, record, fields))
|
|
22
|
+
else
|
|
23
|
+
emit(Records::LazyEmitInput.with_severity(severity, Core.emit_input(record, fields)), &)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def eager_severity_input(severity, record, fields)
|
|
28
|
+
input = if record.equal?(UNSET)
|
|
29
|
+
fields
|
|
30
|
+
elsif fields.empty?
|
|
31
|
+
{ message: record.to_s }
|
|
32
|
+
else
|
|
33
|
+
Core.emit_input(record, fields)
|
|
34
|
+
end
|
|
35
|
+
input.delete("severity")
|
|
36
|
+
input[:severity] = severity
|
|
37
|
+
input
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def with_cleared_configure_guard
|
|
41
|
+
previous_guard = Fiber[Runtime::CONFIGURE_GUARD_KEY]
|
|
42
|
+
Fiber[Runtime::CONFIGURE_GUARD_KEY] = nil
|
|
43
|
+
yield
|
|
44
|
+
ensure
|
|
45
|
+
Fiber[Runtime::CONFIGURE_GUARD_KEY] = previous_guard
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
5
49
|
module FacadeMethods
|
|
50
|
+
include FacadePrivateMethods
|
|
51
|
+
|
|
6
52
|
def runtime(name = :default)
|
|
7
|
-
|
|
53
|
+
RuntimeRegistry.fetch(name)
|
|
8
54
|
end
|
|
9
55
|
|
|
10
56
|
def config = runtime.config
|
|
11
|
-
|
|
12
57
|
def configure(&) = runtime.configure(&)
|
|
13
|
-
|
|
14
58
|
def context = runtime.context
|
|
15
|
-
|
|
16
59
|
def attributes = runtime.attributes
|
|
17
|
-
|
|
18
60
|
def carry = runtime.carry
|
|
19
|
-
|
|
20
61
|
def current_execution = runtime.current_execution
|
|
21
|
-
|
|
22
62
|
def current_execution? = runtime.current_execution?
|
|
23
63
|
|
|
24
|
-
def emit(record =
|
|
64
|
+
def emit(record = UNSET, **fields, &)
|
|
25
65
|
runtime.emit(record, **fields, &)
|
|
26
66
|
end
|
|
27
67
|
|
|
28
|
-
def debug(record =
|
|
68
|
+
def debug(record = UNSET, **fields, &)
|
|
29
69
|
emit_with_severity(:debug, record, fields, &)
|
|
30
70
|
end
|
|
31
71
|
|
|
32
|
-
def info(record =
|
|
72
|
+
def info(record = UNSET, **fields, &)
|
|
33
73
|
emit_with_severity(:info, record, fields, &)
|
|
34
74
|
end
|
|
35
75
|
|
|
36
|
-
def warn(record =
|
|
76
|
+
def warn(record = UNSET, **fields, &)
|
|
37
77
|
emit_with_severity(:warn, record, fields, &)
|
|
38
78
|
end
|
|
39
79
|
|
|
40
|
-
def error(record =
|
|
80
|
+
def error(record = UNSET, **fields, &)
|
|
41
81
|
emit_with_severity(:error, record, fields, &)
|
|
42
82
|
end
|
|
43
83
|
|
|
44
|
-
def fatal(record =
|
|
84
|
+
def fatal(record = UNSET, **fields, &)
|
|
45
85
|
emit_with_severity(:fatal, record, fields, &)
|
|
46
86
|
end
|
|
47
87
|
|
|
48
|
-
def unknown(record =
|
|
88
|
+
def unknown(record = UNSET, **fields, &)
|
|
49
89
|
emit_with_severity(:unknown, record, fields, &)
|
|
50
90
|
end
|
|
51
91
|
|
|
52
|
-
def flush(timeout:
|
|
92
|
+
def flush(timeout: UNSET)
|
|
53
93
|
runtime.flush(timeout: timeout)
|
|
54
94
|
end
|
|
55
95
|
|
|
@@ -62,26 +102,26 @@ module Julewire
|
|
|
62
102
|
def measure_start(key) = summary.measure_start(key)
|
|
63
103
|
|
|
64
104
|
def doctor(name = :default)
|
|
65
|
-
|
|
105
|
+
Diagnostics::Doctor.call(runtime(name))
|
|
66
106
|
end
|
|
67
107
|
|
|
68
108
|
def tail(name = :default, **)
|
|
69
|
-
|
|
109
|
+
Diagnostics::Tail.attach!(runtime(name), **)
|
|
70
110
|
end
|
|
71
111
|
|
|
72
112
|
def observe_self!(name = :default, **)
|
|
73
|
-
|
|
113
|
+
Diagnostics::MetaObserver.attach!(name, **)
|
|
74
114
|
end
|
|
75
115
|
|
|
76
|
-
def dev!(name = :default, output: $stdout, color:
|
|
77
|
-
color = output.respond_to?(:tty?) ? output.tty? : true if color.equal?(
|
|
116
|
+
def dev!(name = :default, output: $stdout, color: UNSET, chaos: false, banner: chaos, tail: true)
|
|
117
|
+
color = output.respond_to?(:tty?) ? output.tty? : true if color.equal?(UNSET)
|
|
78
118
|
punk!(name, output: output, color: color, chaos: chaos, banner: banner)
|
|
79
119
|
return unless tail
|
|
80
120
|
|
|
81
121
|
tail_options = tail == true ? {} : tail
|
|
82
122
|
raise ArgumentError, "tail must be true, false, or an options Hash" unless tail_options.is_a?(Hash)
|
|
83
123
|
|
|
84
|
-
|
|
124
|
+
Tail.attach!(runtime(name), **tail_options)
|
|
85
125
|
end
|
|
86
126
|
|
|
87
127
|
def punk!(name = :default, output: $stdout, color: true, chaos: false, banner: chaos)
|
|
@@ -102,21 +142,19 @@ module Julewire
|
|
|
102
142
|
def fiber(**, &)
|
|
103
143
|
raise ArgumentError, "block required" unless block_given?
|
|
104
144
|
|
|
105
|
-
envelope =
|
|
145
|
+
envelope = Propagation.capture_local
|
|
106
146
|
Fiber.new(**) do |*args|
|
|
107
147
|
with_cleared_configure_guard do
|
|
108
|
-
|
|
148
|
+
Propagation.restore(envelope, owned: true) { yield(*args) }
|
|
109
149
|
end
|
|
110
150
|
end
|
|
111
151
|
end
|
|
112
152
|
|
|
113
153
|
def labels = runtime.labels
|
|
114
|
-
|
|
115
154
|
def after_fork! = runtime.after_fork!
|
|
155
|
+
def reset! = runtime.reset_facade!
|
|
116
156
|
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
def close(timeout: Core::UNSET)
|
|
157
|
+
def close(timeout: UNSET)
|
|
120
158
|
runtime.close(timeout: timeout)
|
|
121
159
|
end
|
|
122
160
|
|
|
@@ -129,10 +167,10 @@ module Julewire
|
|
|
129
167
|
def thread(*, &)
|
|
130
168
|
raise ArgumentError, "block required" unless block_given?
|
|
131
169
|
|
|
132
|
-
envelope =
|
|
170
|
+
envelope = Propagation.capture_local
|
|
133
171
|
Thread.new(*) do |*thread_args|
|
|
134
172
|
with_cleared_configure_guard do
|
|
135
|
-
|
|
173
|
+
Propagation.restore(envelope, owned: true) { yield(*thread_args) }
|
|
136
174
|
end
|
|
137
175
|
end
|
|
138
176
|
end
|
|
@@ -140,42 +178,6 @@ module Julewire
|
|
|
140
178
|
def with_execution(type:, **, &)
|
|
141
179
|
runtime.with_execution(type: type, **, &)
|
|
142
180
|
end
|
|
143
|
-
|
|
144
|
-
private
|
|
145
|
-
|
|
146
|
-
def punk_banner
|
|
147
|
-
"!!JULEWIRE PUNK!! chaos containment armed\n"
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def punk_chaos_output(output, chaos)
|
|
151
|
-
options = chaos.is_a?(Hash) ? chaos : {}
|
|
152
|
-
Core::Destinations::ChaosOutput.new(output, **options)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def emit_with_severity(severity, record, fields, &)
|
|
156
|
-
if record.equal?(Core::UNSET)
|
|
157
|
-
fields.delete("severity")
|
|
158
|
-
fields[:severity] = severity
|
|
159
|
-
runtime.emit(fields, &)
|
|
160
|
-
elsif !block_given? && !record.is_a?(Hash)
|
|
161
|
-
# Scalar eager logs stay allocation-light; lazy inputs need the wrapper
|
|
162
|
-
# so block-built records can still receive the eager severity.
|
|
163
|
-
input = fields.empty? ? { message: record.to_s } : Core.emit_input(record, fields)
|
|
164
|
-
input.delete("severity")
|
|
165
|
-
input[:severity] = severity
|
|
166
|
-
runtime.emit(input)
|
|
167
|
-
else
|
|
168
|
-
runtime.emit(Core::Records::LazyEmitInput.with_severity(severity, Core.emit_input(record, fields)), &)
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def with_cleared_configure_guard
|
|
173
|
-
previous_guard = Fiber[Core::Runtime::CONFIGURE_GUARD_KEY]
|
|
174
|
-
Fiber[Core::Runtime::CONFIGURE_GUARD_KEY] = nil
|
|
175
|
-
yield
|
|
176
|
-
ensure
|
|
177
|
-
Fiber[Core::Runtime::CONFIGURE_GUARD_KEY] = previous_guard
|
|
178
|
-
end
|
|
179
181
|
end
|
|
180
182
|
end
|
|
181
183
|
end
|
|
@@ -26,35 +26,31 @@ module Julewire
|
|
|
26
26
|
source
|
|
27
27
|
].freeze
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
stack: false,
|
|
43
|
-
**capabilities
|
|
44
|
-
)
|
|
45
|
-
end
|
|
29
|
+
definition = lambda do |name, **capabilities|
|
|
30
|
+
Definition.new(
|
|
31
|
+
name: name,
|
|
32
|
+
record_hash: true,
|
|
33
|
+
transform_container: true,
|
|
34
|
+
app_write: false,
|
|
35
|
+
integration_write: false,
|
|
36
|
+
propagate: false,
|
|
37
|
+
emit_by_default: true,
|
|
38
|
+
delete_paths: false,
|
|
39
|
+
stack: false,
|
|
40
|
+
**capabilities
|
|
41
|
+
)
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
DEFINITIONS = {
|
|
49
|
-
execution:
|
|
50
|
-
context:
|
|
45
|
+
execution: definition.call(:execution, integration_write: true, propagate: true),
|
|
46
|
+
context: definition.call(
|
|
51
47
|
:context,
|
|
52
48
|
app_write: true,
|
|
53
49
|
integration_write: true,
|
|
54
50
|
propagate: true,
|
|
55
51
|
stack: true
|
|
56
52
|
),
|
|
57
|
-
carry:
|
|
53
|
+
carry: definition.call(
|
|
58
54
|
:carry,
|
|
59
55
|
app_write: true,
|
|
60
56
|
integration_write: true,
|
|
@@ -63,18 +59,18 @@ module Julewire
|
|
|
63
59
|
delete_paths: true,
|
|
64
60
|
stack: true
|
|
65
61
|
),
|
|
66
|
-
neutral:
|
|
67
|
-
attributes:
|
|
62
|
+
neutral: definition.call(:neutral, integration_write: true, emit_by_default: false, stack: true),
|
|
63
|
+
attributes: definition.call(
|
|
68
64
|
:attributes,
|
|
69
65
|
app_write: true,
|
|
70
66
|
integration_write: true,
|
|
71
67
|
stack: true
|
|
72
68
|
),
|
|
73
|
-
labels:
|
|
74
|
-
payload:
|
|
75
|
-
metrics:
|
|
76
|
-
error:
|
|
77
|
-
summary:
|
|
69
|
+
labels: definition.call(:labels),
|
|
70
|
+
payload: definition.call(:payload),
|
|
71
|
+
metrics: definition.call(:metrics),
|
|
72
|
+
error: definition.call(:error, record_hash: false),
|
|
73
|
+
summary: definition.call(
|
|
78
74
|
:summary,
|
|
79
75
|
record_hash: false,
|
|
80
76
|
transform_container: false,
|
|
@@ -5,16 +5,22 @@ module Julewire
|
|
|
5
5
|
module Fields
|
|
6
6
|
# @api integration_spi
|
|
7
7
|
module FieldSet
|
|
8
|
+
Serializer = Serialization::Serializer
|
|
9
|
+
TRUNCATION_METADATA_KEY = Serializer::TRUNCATION_METADATA_KEY.to_sym
|
|
10
|
+
RESERVED_TRUNCATION_METADATA_ERROR =
|
|
11
|
+
"#{Serializer::TRUNCATION_METADATA_KEY} is reserved for Julewire truncation metadata".freeze
|
|
12
|
+
INVALID_MODES = %i[ignore raise wrap].freeze
|
|
8
13
|
VALUE_KEY = :value
|
|
14
|
+
private_constant :Serializer, :TRUNCATION_METADATA_KEY, :RESERVED_TRUNCATION_METADATA_ERROR, :INVALID_MODES
|
|
9
15
|
|
|
10
16
|
class << self
|
|
11
17
|
# Public ingress accepts String or Symbol keys. Core stores Symbol keys
|
|
12
18
|
# after normalization so extension contracts stay simple.
|
|
13
|
-
def coerce(fields = nil, keyword_fields =
|
|
19
|
+
def coerce(fields = nil, keyword_fields = nil, invalid: :ignore)
|
|
20
|
+
validate_invalid_mode!(invalid)
|
|
14
21
|
coerced = {}
|
|
15
22
|
coerce_fields!(coerced, fields, invalid: invalid) unless fields.nil?
|
|
16
|
-
merge!(coerced, keyword_fields)
|
|
17
|
-
coerced
|
|
23
|
+
merge!(coerced, keyword_fields)
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
def merge(left, right)
|
|
@@ -25,51 +31,81 @@ module Julewire
|
|
|
25
31
|
return target unless fields.is_a?(Hash)
|
|
26
32
|
|
|
27
33
|
fields.each do |key, value|
|
|
28
|
-
target[
|
|
34
|
+
target[normalized_field_key(key)] = copy_field_value(value)
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
target
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
def deep_dup(value)
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
deep_dup_with(value, preserve_truncation_metadata: false)
|
|
42
|
+
end
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
def deep_dup_owned(value)
|
|
45
|
+
deep_dup_with(value, preserve_truncation_metadata: true)
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
def deep_symbolize_keys(value)
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
deep_symbolize_keys_with(value, preserve_truncation_metadata: false)
|
|
50
|
+
end
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
def deep_symbolize_owned_keys(value)
|
|
53
|
+
deep_symbolize_keys_with(value, preserve_truncation_metadata: true)
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
def frozen_copy(value)
|
|
49
|
-
|
|
57
|
+
Internal.frozen_copy(value)
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def value_for(hash, key, default: nil)
|
|
53
61
|
return default unless hash.is_a?(Hash)
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
return hash[normalized] if hash.key?(normalized)
|
|
57
|
-
|
|
58
|
-
default
|
|
63
|
+
hash.fetch(Internal.normalize_key(key), default)
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
private
|
|
62
67
|
|
|
68
|
+
def deep_dup_with(value, preserve_truncation_metadata:)
|
|
69
|
+
Serialization::ValueCopy.call(
|
|
70
|
+
value,
|
|
71
|
+
preserve_truncation_metadata: preserve_truncation_metadata
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def deep_symbolize_keys_with(value, preserve_truncation_metadata:)
|
|
76
|
+
Serialization::ValueCopy.call(
|
|
77
|
+
value,
|
|
78
|
+
max_array_items: Serializer::DEFAULT_MAX_ARRAY_ITEMS,
|
|
79
|
+
max_hash_keys: Serializer::DEFAULT_MAX_HASH_KEYS,
|
|
80
|
+
max_string_bytes: Serializer::DEFAULT_MAX_STRING_BYTES,
|
|
81
|
+
preserve_truncation_metadata: preserve_truncation_metadata,
|
|
82
|
+
symbolize_keys: true
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
63
86
|
def coerce_fields!(target, fields, invalid:)
|
|
64
87
|
if fields.is_a?(Hash)
|
|
65
88
|
merge!(target, fields)
|
|
66
89
|
elsif invalid == :wrap
|
|
67
|
-
target[VALUE_KEY] =
|
|
90
|
+
target[VALUE_KEY] = copy_field_value(fields)
|
|
68
91
|
elsif invalid == :raise
|
|
69
92
|
raise ArgumentError, "fields must be a Hash"
|
|
70
93
|
end
|
|
71
94
|
end
|
|
72
95
|
|
|
96
|
+
def validate_invalid_mode!(invalid)
|
|
97
|
+
return if INVALID_MODES.include?(invalid)
|
|
98
|
+
|
|
99
|
+
raise ArgumentError, "invalid field coercion mode: #{invalid.inspect}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def normalized_field_key(key)
|
|
103
|
+
normalized_key = Internal.normalize_key(key)
|
|
104
|
+
raise ArgumentError, RESERVED_TRUNCATION_METADATA_ERROR if normalized_key == TRUNCATION_METADATA_KEY
|
|
105
|
+
|
|
106
|
+
normalized_key
|
|
107
|
+
end
|
|
108
|
+
|
|
73
109
|
def copy_field_value(value) = deep_symbolize_keys(value)
|
|
74
110
|
end
|
|
75
111
|
end
|