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
|
@@ -28,7 +28,7 @@ module Julewire
|
|
|
28
28
|
max_array_items: DEFAULT_MAX_ARRAY_ITEMS,
|
|
29
29
|
max_hash_keys: DEFAULT_MAX_HASH_KEYS,
|
|
30
30
|
compact_empty: false,
|
|
31
|
-
max_backtrace_lines:
|
|
31
|
+
max_backtrace_lines: MAX_BACKTRACE_LINES
|
|
32
32
|
)
|
|
33
33
|
new(
|
|
34
34
|
max_depth: max_depth,
|
|
@@ -47,8 +47,7 @@ module Julewire
|
|
|
47
47
|
max_array_items: DEFAULT_MAX_ARRAY_ITEMS,
|
|
48
48
|
max_hash_keys: DEFAULT_MAX_HASH_KEYS,
|
|
49
49
|
compact_empty: false,
|
|
50
|
-
max_backtrace_lines:
|
|
51
|
-
copy_strings: true
|
|
50
|
+
max_backtrace_lines: MAX_BACKTRACE_LINES
|
|
52
51
|
)
|
|
53
52
|
super(
|
|
54
53
|
max_array_items: max_array_items,
|
|
@@ -63,7 +62,6 @@ module Julewire
|
|
|
63
62
|
name: :max_backtrace_lines
|
|
64
63
|
)
|
|
65
64
|
@compact_empty = compact_empty
|
|
66
|
-
@copy_strings = copy_strings
|
|
67
65
|
end
|
|
68
66
|
|
|
69
67
|
def serialize(value)
|
|
@@ -77,27 +75,25 @@ module Julewire
|
|
|
77
75
|
|
|
78
76
|
private
|
|
79
77
|
|
|
80
|
-
def scalar_value(value, depth
|
|
78
|
+
def scalar_value(value, depth)
|
|
81
79
|
return serialize_exception(value, depth) if value.is_a?(Exception)
|
|
82
80
|
|
|
83
81
|
case value
|
|
84
82
|
when nil, true, false
|
|
85
|
-
|
|
83
|
+
value
|
|
86
84
|
when Numeric
|
|
87
85
|
serialize_numeric(value)
|
|
88
86
|
when Symbol
|
|
89
|
-
|
|
87
|
+
value.to_s
|
|
90
88
|
when String
|
|
91
89
|
serialize_string(value)
|
|
92
|
-
when Time,
|
|
90
|
+
when Time, Date
|
|
93
91
|
serialize_temporal(value)
|
|
94
92
|
else
|
|
95
93
|
return serialize_iso8601_temporal(value) if zone_temporal?(value)
|
|
96
94
|
|
|
97
95
|
serialize_object(value)
|
|
98
96
|
end
|
|
99
|
-
rescue StandardError => e
|
|
100
|
-
clear_truncated(unserializable_marker(e))
|
|
101
97
|
end
|
|
102
98
|
|
|
103
99
|
def serialize_exception(error, depth)
|
|
@@ -116,30 +112,30 @@ module Julewire
|
|
|
116
112
|
end
|
|
117
113
|
|
|
118
114
|
def serialize_numeric(value)
|
|
119
|
-
return serialize_float(value) if value.
|
|
120
|
-
return
|
|
121
|
-
return serialize_string(value.to_s("F")) if defined?(BigDecimal) && value.
|
|
115
|
+
return serialize_float(value) if value.instance_of?(Float)
|
|
116
|
+
return value if value.instance_of?(Integer)
|
|
117
|
+
return serialize_string(value.to_s("F")) if defined?(BigDecimal) && value.instance_of?(::BigDecimal)
|
|
122
118
|
|
|
123
|
-
serialize_string(
|
|
119
|
+
serialize_string(value.to_s)
|
|
124
120
|
end
|
|
125
121
|
|
|
126
122
|
def serialize_float(value)
|
|
127
|
-
return
|
|
128
|
-
return
|
|
123
|
+
return value if value.finite?
|
|
124
|
+
return NAN_VALUE if value.nan?
|
|
129
125
|
|
|
130
|
-
|
|
126
|
+
value.positive? ? INFINITY_VALUE : NEGATIVE_INFINITY_VALUE
|
|
131
127
|
end
|
|
132
128
|
|
|
133
129
|
def serialize_temporal(value)
|
|
134
|
-
return
|
|
135
|
-
return
|
|
130
|
+
return value.getutc.iso8601(9) if value.is_a?(Time)
|
|
131
|
+
return value.iso8601(9) if value.is_a?(DateTime)
|
|
136
132
|
|
|
137
|
-
|
|
133
|
+
value.iso8601
|
|
138
134
|
end
|
|
139
135
|
|
|
140
136
|
def serialize_iso8601_temporal(value)
|
|
141
137
|
temporal = value.respond_to?(:utc) ? value.utc : value
|
|
142
|
-
|
|
138
|
+
EncodingSanitizer.call(temporal.iso8601(9))
|
|
143
139
|
end
|
|
144
140
|
|
|
145
141
|
def zone_temporal?(value)
|
|
@@ -154,7 +150,7 @@ module Julewire
|
|
|
154
150
|
|
|
155
151
|
def key_value(key) = serialize_key(key)
|
|
156
152
|
|
|
157
|
-
def error_value(error) =
|
|
153
|
+
def error_value(error) = unserializable_marker(error)
|
|
158
154
|
|
|
159
155
|
def serialize_key(key)
|
|
160
156
|
case key
|
|
@@ -170,31 +166,18 @@ module Julewire
|
|
|
170
166
|
end
|
|
171
167
|
|
|
172
168
|
def serialize_symbol_key(key)
|
|
173
|
-
|
|
174
|
-
return serialize_trusted_key_string(name) if safe_trusted_key_name?(name)
|
|
175
|
-
|
|
176
|
-
serialize_key_string(name)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def safe_trusted_key_name?(value)
|
|
180
|
-
value.ascii_only? || (value.encoding == Encoding::UTF_8 && value.valid_encoding?)
|
|
169
|
+
serialize_key_string(key.name)
|
|
181
170
|
end
|
|
182
171
|
|
|
183
172
|
def serialize_key_string(value)
|
|
184
173
|
string = EncodingSanitizer.call(value)
|
|
185
|
-
return
|
|
174
|
+
return copy_string(string) if string.bytesize <= MAX_KEY_BYTES
|
|
186
175
|
|
|
187
176
|
mark_truncated("#{string.byteslice(0, MAX_KEY_BYTES).scrub("?")}#{TRUNCATED_SUFFIX}")
|
|
188
177
|
end
|
|
189
178
|
|
|
190
|
-
def serialize_trusted_key_string(value)
|
|
191
|
-
return clear_truncated(value) if value.bytesize <= MAX_KEY_BYTES
|
|
192
|
-
|
|
193
|
-
mark_truncated("#{value.byteslice(0, MAX_KEY_BYTES).scrub("?")}#{TRUNCATED_SUFFIX}")
|
|
194
|
-
end
|
|
195
|
-
|
|
196
179
|
def serialize_object(value)
|
|
197
|
-
|
|
180
|
+
object_marker(value)
|
|
198
181
|
end
|
|
199
182
|
|
|
200
183
|
def object_marker(value)
|
|
@@ -213,20 +196,10 @@ module Julewire
|
|
|
213
196
|
|
|
214
197
|
def serialize_string(value)
|
|
215
198
|
string = EncodingSanitizer.call(value)
|
|
216
|
-
return
|
|
199
|
+
return copy_string(string) if string.bytesize <= @max_string_bytes
|
|
217
200
|
|
|
218
201
|
mark_truncated("#{string.byteslice(0, @max_string_bytes).scrub("?")}#{TRUNCATED_SUFFIX}")
|
|
219
202
|
end
|
|
220
|
-
|
|
221
|
-
def copy_string(value)
|
|
222
|
-
value.frozen? || !@copy_strings ? value : value.dup
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def record_hash_truncation(fields, _raw_key, key, key_truncated, child_truncated)
|
|
226
|
-
return fields unless key_truncated || child_truncated
|
|
227
|
-
|
|
228
|
-
append_truncation_field(fields, key)
|
|
229
|
-
end
|
|
230
203
|
end
|
|
231
204
|
end
|
|
232
205
|
end
|
|
@@ -14,6 +14,15 @@ module Julewire
|
|
|
14
14
|
end
|
|
15
15
|
pool[serializer_key] ||= yield
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def serialize(pool_key, serializer_key, payload, &)
|
|
19
|
+
serialize_with(serializer(pool_key, serializer_key, &), payload, &)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def serialize_with(serializer, payload)
|
|
23
|
+
serializer = yield if serializer.in_use?
|
|
24
|
+
serializer.serialize(payload)
|
|
25
|
+
end
|
|
17
26
|
end
|
|
18
27
|
end
|
|
19
28
|
end
|
|
@@ -48,13 +48,13 @@ module Julewire
|
|
|
48
48
|
positive: true
|
|
49
49
|
)
|
|
50
50
|
@color = color
|
|
51
|
-
@
|
|
51
|
+
@append_newline = append_newline
|
|
52
52
|
@theme = validate_theme(theme)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def call(payload)
|
|
56
56
|
text = payload.is_a?(String) ? payload : line_for(payload)
|
|
57
|
-
"#{text}
|
|
57
|
+
@append_newline ? "#{text}\n" : text
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
private
|
|
@@ -82,7 +82,7 @@ module Julewire
|
|
|
82
82
|
# Console output is human-facing; JSON keeps nanosecond precision.
|
|
83
83
|
return value.iso8601(6) if value.respond_to?(:iso8601)
|
|
84
84
|
|
|
85
|
-
value
|
|
85
|
+
value
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def severity(payload)
|
|
@@ -123,7 +123,7 @@ module Julewire
|
|
|
123
123
|
def compact_hash(name, value)
|
|
124
124
|
return unless value.is_a?(Hash) && !value.empty?
|
|
125
125
|
|
|
126
|
-
"#{name}=#{truncate(JSON.generate(value
|
|
126
|
+
"#{name}=#{truncate(JSON.generate(value))}"
|
|
127
127
|
rescue StandardError
|
|
128
128
|
"#{name}=#{truncate(value.inspect)}"
|
|
129
129
|
end
|
|
@@ -135,7 +135,7 @@ module Julewire
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
def value_at(payload, key)
|
|
138
|
-
Fields::Lookup.
|
|
138
|
+
Fields::Lookup.wire_value(payload, key)
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def blank?(value)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module Core
|
|
5
|
+
module Serialization
|
|
6
|
+
module TruncationMetadata
|
|
7
|
+
NAMES = %i[truncated truncated_fields limits max_array_items max_depth max_hash_keys max_string_bytes].freeze
|
|
8
|
+
KEYS = {
|
|
9
|
+
string: NAMES.to_h { [it, it.to_s] }.freeze,
|
|
10
|
+
symbol: NAMES.to_h { [it, it] }.freeze
|
|
11
|
+
}.freeze
|
|
12
|
+
METADATA_KEYS = KEYS.fetch(:symbol).values_at(:truncated, :truncated_fields, :limits).freeze
|
|
13
|
+
METADATA_KEY_NAMES = KEYS.fetch(:string).values_at(:truncated, :truncated_fields, :limits).freeze
|
|
14
|
+
LIMIT_KEYS = KEYS.fetch(:symbol).values_at(
|
|
15
|
+
:max_array_items,
|
|
16
|
+
:max_depth,
|
|
17
|
+
:max_hash_keys,
|
|
18
|
+
:max_string_bytes
|
|
19
|
+
).freeze
|
|
20
|
+
LIMIT_KEY_NAMES = KEYS.fetch(:string).values_at(
|
|
21
|
+
:max_array_items,
|
|
22
|
+
:max_depth,
|
|
23
|
+
:max_hash_keys,
|
|
24
|
+
:max_string_bytes
|
|
25
|
+
).freeze
|
|
26
|
+
SYMBOL_METADATA_KEYS = KEYS.fetch(:symbol).merge(limit_keys: LIMIT_KEYS).freeze
|
|
27
|
+
STRING_METADATA_KEYS = KEYS.fetch(:string).merge(limit_keys: LIMIT_KEY_NAMES).freeze
|
|
28
|
+
[
|
|
29
|
+
NAMES,
|
|
30
|
+
KEYS,
|
|
31
|
+
METADATA_KEYS,
|
|
32
|
+
METADATA_KEY_NAMES,
|
|
33
|
+
LIMIT_KEYS,
|
|
34
|
+
LIMIT_KEY_NAMES,
|
|
35
|
+
SYMBOL_METADATA_KEYS,
|
|
36
|
+
STRING_METADATA_KEYS
|
|
37
|
+
].each do |constant|
|
|
38
|
+
::Ractor.make_shareable(constant) if defined?(::Ractor) && ::Ractor.respond_to?(:make_shareable)
|
|
39
|
+
end
|
|
40
|
+
private_constant :NAMES, :KEYS, :METADATA_KEYS, :METADATA_KEY_NAMES, :LIMIT_KEYS, :LIMIT_KEY_NAMES,
|
|
41
|
+
:SYMBOL_METADATA_KEYS, :STRING_METADATA_KEYS
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
def build(fields, max_array_items:, max_depth:, max_hash_keys:, max_string_bytes:, key_style: :string,
|
|
45
|
+
compact_limits: false, freeze_values: false)
|
|
46
|
+
keys = KEYS.fetch(key_style)
|
|
47
|
+
limits = limits_hash(
|
|
48
|
+
keys,
|
|
49
|
+
max_array_items: max_array_items,
|
|
50
|
+
max_depth: max_depth,
|
|
51
|
+
max_hash_keys: max_hash_keys,
|
|
52
|
+
max_string_bytes: max_string_bytes,
|
|
53
|
+
compact_limits: compact_limits
|
|
54
|
+
)
|
|
55
|
+
metadata = {
|
|
56
|
+
keys.fetch(:truncated) => true,
|
|
57
|
+
keys.fetch(:truncated_fields) => field_list(fields),
|
|
58
|
+
keys.fetch(:limits) => limits
|
|
59
|
+
}
|
|
60
|
+
freeze_values ? deep_freeze(metadata, keys) : metadata
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def append_field(fields, field)
|
|
64
|
+
fields ||= []
|
|
65
|
+
fields << field unless fields.include?(field)
|
|
66
|
+
fields
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def valid?(value, max_fields: nil)
|
|
70
|
+
return false unless value.is_a?(Hash)
|
|
71
|
+
|
|
72
|
+
keys = metadata_keys(value)
|
|
73
|
+
return false unless keys
|
|
74
|
+
return false unless value.fetch(keys.fetch(:truncated)) == true
|
|
75
|
+
|
|
76
|
+
valid_fields?(value.fetch(keys.fetch(:truncated_fields)), max_fields: max_fields) &&
|
|
77
|
+
valid_limits?(value.fetch(keys.fetch(:limits)), limit_keys: keys.fetch(:limit_keys))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def copy(value, freeze_values:, key_style: :symbol)
|
|
81
|
+
source_keys = metadata_keys(value)
|
|
82
|
+
target_keys = KEYS.fetch(key_style)
|
|
83
|
+
metadata = {
|
|
84
|
+
target_keys.fetch(:truncated) => true,
|
|
85
|
+
target_keys.fetch(:truncated_fields) => copy_fields(value.fetch(source_keys.fetch(:truncated_fields))),
|
|
86
|
+
target_keys.fetch(:limits) => copy_limits(value.fetch(source_keys.fetch(:limits)), source_keys,
|
|
87
|
+
target_keys)
|
|
88
|
+
}
|
|
89
|
+
freeze_values ? deep_freeze(metadata, target_keys) : metadata
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def field_list(fields)
|
|
95
|
+
Array(fields).uniq
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def limits_hash(keys, max_array_items:, max_depth:, max_hash_keys:, max_string_bytes:, compact_limits:)
|
|
99
|
+
limits = {
|
|
100
|
+
keys.fetch(:max_array_items) => max_array_items,
|
|
101
|
+
keys.fetch(:max_depth) => max_depth,
|
|
102
|
+
keys.fetch(:max_hash_keys) => max_hash_keys,
|
|
103
|
+
keys.fetch(:max_string_bytes) => max_string_bytes
|
|
104
|
+
}
|
|
105
|
+
compact_limits ? limits.compact : limits
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def deep_freeze(metadata, keys)
|
|
109
|
+
metadata.fetch(keys.fetch(:truncated_fields)).each(&:freeze)
|
|
110
|
+
metadata.fetch(keys.fetch(:truncated_fields)).freeze
|
|
111
|
+
metadata.fetch(keys.fetch(:limits)).freeze
|
|
112
|
+
metadata.freeze
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def copy_fields(fields)
|
|
116
|
+
fields.map(&:dup)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def copy_limits(limits, source_keys, target_keys)
|
|
120
|
+
LIMIT_KEYS.each_with_object({}) do |name, copied|
|
|
121
|
+
source_key = source_keys.fetch(name)
|
|
122
|
+
next unless limits.key?(source_key)
|
|
123
|
+
|
|
124
|
+
copied[target_keys.fetch(name)] = limits.fetch(source_key)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def metadata_keys(value)
|
|
129
|
+
return SYMBOL_METADATA_KEYS if exact_keys?(value, METADATA_KEYS)
|
|
130
|
+
|
|
131
|
+
STRING_METADATA_KEYS if exact_keys?(value, METADATA_KEY_NAMES)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def exact_keys?(value, keys)
|
|
135
|
+
value.length == keys.length && keys.all? { value.key?(it) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def valid_fields?(fields, max_fields:)
|
|
139
|
+
return false unless fields.is_a?(Array)
|
|
140
|
+
return false if max_fields && fields.length > max_fields
|
|
141
|
+
|
|
142
|
+
fields.all?(String)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def valid_limits?(limits, limit_keys:)
|
|
146
|
+
return false unless limits.is_a?(Hash)
|
|
147
|
+
|
|
148
|
+
limits.all? do |key, value|
|
|
149
|
+
limit_keys.include?(key) && (value.nil? || value.instance_of?(Integer))
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
private_constant :TruncationMetadata
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|