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
|
@@ -4,8 +4,8 @@ module Julewire
|
|
|
4
4
|
module Core
|
|
5
5
|
module Fields
|
|
6
6
|
# @api internal
|
|
7
|
-
# Immutable layers keep snapshots stable while each stack tracks only
|
|
8
|
-
# current head and
|
|
7
|
+
# Immutable layers keep snapshots stable while each stack tracks only the
|
|
8
|
+
# current head and snapshot cache.
|
|
9
9
|
class FieldStack
|
|
10
10
|
EMPTY_HASH = {}.freeze
|
|
11
11
|
private_constant :EMPTY_HASH
|
|
@@ -13,15 +13,29 @@ module Julewire
|
|
|
13
13
|
class Layer
|
|
14
14
|
attr_reader :fields, :parent
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
class << self
|
|
17
|
+
def fields(parent, fields, clear_parent_deletes: true)
|
|
18
|
+
clear_parent_fields = fields if clear_parent_deletes
|
|
19
|
+
new(
|
|
20
|
+
parent,
|
|
21
|
+
fields,
|
|
22
|
+
delete_paths: nil,
|
|
23
|
+
clear_parent_fields:
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def delete_paths(parent, paths)
|
|
28
|
+
new(parent, nil, delete_paths: paths, clear_parent_fields: nil)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private :new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def initialize(parent, fields, delete_paths:, clear_parent_fields:)
|
|
17
35
|
@parent = parent
|
|
18
36
|
@fields = fields
|
|
19
37
|
@delete_paths = delete_paths
|
|
20
|
-
@
|
|
21
|
-
@active_delete_paths_computed = false
|
|
22
|
-
@active_delete_paths = nil
|
|
23
|
-
@snapshot = nil
|
|
24
|
-
@value_cache = nil
|
|
38
|
+
@clear_parent_fields = clear_parent_fields
|
|
25
39
|
end
|
|
26
40
|
|
|
27
41
|
def snapshot
|
|
@@ -29,81 +43,54 @@ module Julewire
|
|
|
29
43
|
end
|
|
30
44
|
|
|
31
45
|
def value_for(key)
|
|
32
|
-
return @value_cache
|
|
46
|
+
return @value_cache.fetch(key) if @value_cache&.key?(key)
|
|
33
47
|
|
|
34
48
|
value = if delete_paths_for_key?(key)
|
|
35
49
|
FieldSet.value_for(snapshot, key, default: MISSING)
|
|
36
50
|
else
|
|
37
51
|
field_value = FieldSet.value_for(@fields, key, default: MISSING)
|
|
38
|
-
field_value.equal?(MISSING) ? parent_value_for(key) :
|
|
52
|
+
field_value.equal?(MISSING) ? parent_value_for(key) : frozen_field_value(field_value)
|
|
39
53
|
end
|
|
40
54
|
(@value_cache ||= {})[key] = value
|
|
41
55
|
end
|
|
42
56
|
|
|
43
57
|
def active_delete_paths
|
|
44
|
-
return @active_delete_paths if
|
|
58
|
+
return @active_delete_paths if instance_variable_defined?(:@active_delete_paths)
|
|
45
59
|
|
|
46
60
|
@active_delete_paths = build_active_delete_paths
|
|
47
|
-
@active_delete_paths_computed = true
|
|
48
|
-
@active_delete_paths
|
|
49
61
|
end
|
|
50
62
|
|
|
51
63
|
def snapshot_cached?
|
|
52
64
|
!@snapshot.nil?
|
|
53
65
|
end
|
|
54
66
|
|
|
55
|
-
def
|
|
56
|
-
|
|
67
|
+
def merge_into(snapshot)
|
|
68
|
+
Internal.merge_owned!(snapshot, FieldSet.deep_dup_owned(@fields))
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
private
|
|
60
72
|
|
|
61
73
|
def build_snapshot
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
paths = source.delete_paths_for_snapshot
|
|
69
|
-
Fields::Internal.apply_delete_paths!(snapshot, paths) if paths
|
|
70
|
-
end
|
|
71
|
-
Fields::Internal.frozen_copy(snapshot)
|
|
74
|
+
sources, base = source_layers_and_base
|
|
75
|
+
snapshot = base ? FieldSet.deep_dup_owned(base.snapshot) : {}
|
|
76
|
+
sources.reverse_each { it.merge_into(snapshot) }
|
|
77
|
+
paths = active_delete_paths
|
|
78
|
+
Internal.apply_delete_paths!(snapshot, paths) if paths
|
|
79
|
+
Internal.frozen_owned_copy(snapshot)
|
|
72
80
|
end
|
|
73
81
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
paths = delete_paths_for_snapshot
|
|
77
|
-
Fields::Internal.apply_delete_paths!(snapshot, paths) if paths
|
|
78
|
-
Fields::Internal.frozen_copy(snapshot)
|
|
82
|
+
def frozen_field_value(value)
|
|
83
|
+
Internal.frozen_owned_copy(value)
|
|
79
84
|
end
|
|
80
85
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
paths = delete_paths_for_snapshot
|
|
84
|
-
Fields::Internal.apply_delete_paths!(snapshot, paths) if paths
|
|
85
|
-
Fields::Internal.frozen_copy(snapshot)
|
|
86
|
-
end
|
|
86
|
+
def source_layers_and_base(source = self, seen_sources = [], sources = [])
|
|
87
|
+
return [sources, source] if source.nil? || source.snapshot_cached?
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
source = source_chain_base
|
|
90
|
-
source ? FieldSet.deep_dup(source.snapshot) : {}
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def source_chain
|
|
94
|
-
sources = []
|
|
95
|
-
source = self
|
|
96
|
-
until source.nil? || source.snapshot_cached?
|
|
97
|
-
sources << source
|
|
98
|
-
source = source.parent
|
|
99
|
-
end
|
|
100
|
-
sources
|
|
101
|
-
end
|
|
89
|
+
raise Error, "field stack layer cycle" if seen_sources.include?(source)
|
|
102
90
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
source
|
|
106
|
-
source
|
|
91
|
+
seen_sources << source
|
|
92
|
+
sources << source
|
|
93
|
+
source_layers_and_base(source.parent, seen_sources, sources)
|
|
107
94
|
end
|
|
108
95
|
|
|
109
96
|
def parent_value_for(key)
|
|
@@ -118,16 +105,14 @@ module Julewire
|
|
|
118
105
|
|
|
119
106
|
def build_active_delete_paths
|
|
120
107
|
paths = @parent&.active_delete_paths
|
|
121
|
-
paths = clear_active_delete_paths(paths) if paths
|
|
108
|
+
paths = clear_active_delete_paths(paths) if paths
|
|
122
109
|
paths = append_delete_paths(paths) if @delete_paths
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
paths.empty? ? nil : paths
|
|
110
|
+
paths
|
|
126
111
|
end
|
|
127
112
|
|
|
128
113
|
def clear_active_delete_paths(paths)
|
|
129
114
|
paths = paths.dup
|
|
130
|
-
|
|
115
|
+
Internal.clear_delete_paths!(paths, @clear_parent_fields)
|
|
131
116
|
paths
|
|
132
117
|
end
|
|
133
118
|
|
|
@@ -137,51 +122,35 @@ module Julewire
|
|
|
137
122
|
end
|
|
138
123
|
private_constant :Layer
|
|
139
124
|
|
|
140
|
-
def initialize(fields =
|
|
125
|
+
def initialize(fields = nil, delete_paths: false, source: nil)
|
|
141
126
|
@source = source
|
|
142
127
|
@delete_paths_enabled = delete_paths
|
|
143
|
-
|
|
144
|
-
@snapshot_version = nil
|
|
145
|
-
@snapshot = nil
|
|
146
|
-
@value_cache = nil
|
|
147
|
-
add(fields) if fields.is_a?(Hash) && !fields.empty?
|
|
128
|
+
add(fields)
|
|
148
129
|
end
|
|
149
130
|
|
|
150
131
|
def snapshot
|
|
151
|
-
return @snapshot if @
|
|
132
|
+
return @snapshot if @snapshot
|
|
152
133
|
|
|
153
134
|
@snapshot = @source ? @source.snapshot : EMPTY_HASH
|
|
154
|
-
@snapshot_version = @version
|
|
155
|
-
@snapshot
|
|
156
135
|
end
|
|
157
136
|
|
|
158
|
-
def
|
|
137
|
+
def branch
|
|
159
138
|
self.class.new(delete_paths: @delete_paths_enabled, source: @source)
|
|
160
139
|
end
|
|
161
140
|
|
|
162
141
|
def value_for(key, default:)
|
|
163
|
-
|
|
164
|
-
return cache[key] if cache&.key?(key)
|
|
165
|
-
|
|
166
|
-
if key.is_a?(String)
|
|
167
|
-
key = Fields::Internal.normalize_key(key)
|
|
168
|
-
cache = @value_cache
|
|
169
|
-
return cache[key] if cache&.key?(key)
|
|
170
|
-
end
|
|
171
|
-
|
|
142
|
+
key = Internal.normalize_key(key)
|
|
172
143
|
value = source_value_for(key)
|
|
173
144
|
return default if value.equal?(MISSING)
|
|
174
145
|
|
|
175
|
-
|
|
146
|
+
value
|
|
176
147
|
end
|
|
177
148
|
|
|
178
149
|
def add(fields = nil, owned: false, **keyword_fields)
|
|
179
150
|
fields = field_input(fields, keyword_fields, owned: owned)
|
|
180
|
-
return unless fields.is_a?(Hash)
|
|
181
151
|
return if fields.empty?
|
|
182
152
|
|
|
183
|
-
|
|
184
|
-
@source = Layer.new(@source, fields, clear_parent_deletes: true)
|
|
153
|
+
@source = Layer.fields(@source, fields)
|
|
185
154
|
invalidate_snapshot!
|
|
186
155
|
end
|
|
187
156
|
|
|
@@ -189,16 +158,14 @@ module Julewire
|
|
|
189
158
|
return if path.empty?
|
|
190
159
|
return unless @delete_paths_enabled
|
|
191
160
|
|
|
192
|
-
@source = Layer.
|
|
161
|
+
@source = Layer.delete_paths(@source, [path])
|
|
193
162
|
invalidate_snapshot!
|
|
194
163
|
end
|
|
195
164
|
|
|
196
165
|
def with(fields = nil, owned: false, **keyword_fields, &)
|
|
197
166
|
fields = field_input(fields, keyword_fields, owned: owned)
|
|
198
|
-
return yield unless fields.is_a?(Hash)
|
|
199
167
|
return yield if fields.empty?
|
|
200
168
|
|
|
201
|
-
fields = normalize_owned_keys(fields) if owned
|
|
202
169
|
with_layer(fields, &)
|
|
203
170
|
end
|
|
204
171
|
|
|
@@ -207,28 +174,25 @@ module Julewire
|
|
|
207
174
|
|
|
208
175
|
return yield unless @delete_paths_enabled
|
|
209
176
|
|
|
210
|
-
|
|
177
|
+
with_delete_layer([path], &)
|
|
211
178
|
end
|
|
212
179
|
|
|
213
180
|
private
|
|
214
181
|
|
|
215
182
|
def field_input(fields, keyword_fields, owned:)
|
|
216
183
|
if owned
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return fields.merge(keyword_fields) if fields.is_a?(Hash)
|
|
184
|
+
fields = keyword_fields if fields.nil?
|
|
185
|
+
Serialization::DeepFreeze.validate_symbol_hash(fields)
|
|
220
186
|
|
|
221
|
-
return keyword_fields
|
|
187
|
+
return fields.merge(keyword_fields)
|
|
222
188
|
end
|
|
223
189
|
|
|
224
|
-
return FieldSet.deep_symbolize_keys(fields) if keyword_fields.empty?
|
|
225
|
-
|
|
226
190
|
FieldSet.coerce(fields, keyword_fields)
|
|
227
191
|
end
|
|
228
192
|
|
|
229
|
-
def with_layer(fields
|
|
193
|
+
def with_layer(fields)
|
|
230
194
|
previous_source = @source
|
|
231
|
-
@source = Layer.
|
|
195
|
+
@source = Layer.fields(previous_source, fields, clear_parent_deletes: false)
|
|
232
196
|
invalidate_snapshot!
|
|
233
197
|
begin
|
|
234
198
|
yield
|
|
@@ -238,30 +202,26 @@ module Julewire
|
|
|
238
202
|
end
|
|
239
203
|
end
|
|
240
204
|
|
|
241
|
-
def
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
205
|
+
def with_delete_layer(delete_paths)
|
|
206
|
+
previous_source = @source
|
|
207
|
+
@source = Layer.delete_paths(previous_source, delete_paths)
|
|
208
|
+
invalidate_snapshot!
|
|
209
|
+
begin
|
|
210
|
+
yield
|
|
211
|
+
ensure
|
|
212
|
+
@source = previous_source
|
|
213
|
+
invalidate_snapshot!
|
|
214
|
+
end
|
|
245
215
|
end
|
|
246
216
|
|
|
247
217
|
def source_value_for(key)
|
|
248
218
|
return MISSING unless @source
|
|
249
219
|
|
|
250
|
-
|
|
251
|
-
# Single-layer hits avoid Layer's delete-path/cache bookkeeping.
|
|
252
|
-
field_value = FieldSet.value_for(@source.fields, key, default: MISSING)
|
|
253
|
-
return Fields::Internal.frozen_copy(field_value) unless field_value.equal?(MISSING)
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
value = @source.value_for(key)
|
|
257
|
-
value.equal?(MISSING) ? MISSING : value
|
|
220
|
+
@source.value_for(key)
|
|
258
221
|
end
|
|
259
222
|
|
|
260
223
|
def invalidate_snapshot!
|
|
261
|
-
@version += 1
|
|
262
224
|
@snapshot = nil
|
|
263
|
-
@snapshot_version = nil
|
|
264
|
-
@value_cache = nil
|
|
265
225
|
end
|
|
266
226
|
end
|
|
267
227
|
end
|
|
@@ -11,12 +11,10 @@ module Julewire
|
|
|
11
11
|
return target if normalized_path.empty?
|
|
12
12
|
|
|
13
13
|
deep_delete_path!(target, normalized_path)
|
|
14
|
-
target
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def apply_delete_paths!(target, paths)
|
|
18
17
|
paths.each { delete_path!(target, it) }
|
|
19
|
-
target
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
def clear_delete_paths!(paths, fields)
|
|
@@ -27,7 +25,7 @@ module Julewire
|
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def normalize_path(path)
|
|
30
|
-
Array(path).flatten.
|
|
28
|
+
Array(path).flatten.map { Internal.normalize_key(it) }
|
|
31
29
|
end
|
|
32
30
|
|
|
33
31
|
private
|
|
@@ -37,7 +35,7 @@ module Julewire
|
|
|
37
35
|
|
|
38
36
|
fields.flat_map do |key, value|
|
|
39
37
|
path = prefix + [Internal.normalize_key(key)]
|
|
40
|
-
nested =
|
|
38
|
+
nested = field_paths(value, path)
|
|
41
39
|
nested.empty? ? [path] : nested
|
|
42
40
|
end
|
|
43
41
|
end
|
|
@@ -53,12 +51,11 @@ module Julewire
|
|
|
53
51
|
key = path.first
|
|
54
52
|
if path.one?
|
|
55
53
|
Internal.delete_key!(target, key)
|
|
56
|
-
|
|
54
|
+
else
|
|
55
|
+
child = FieldSet.value_for(target, key)
|
|
56
|
+
deep_delete_path!(child, path.drop(1))
|
|
57
|
+
Internal.delete_key!(target, key) if child.is_a?(Hash) && child.empty?
|
|
57
58
|
end
|
|
58
|
-
|
|
59
|
-
child = FieldSet.value_for(target, key)
|
|
60
|
-
deep_delete_path!(child, path.drop(1))
|
|
61
|
-
Internal.delete_key!(target, key) if child.is_a?(Hash) && child.empty?
|
|
62
59
|
end
|
|
63
60
|
end
|
|
64
61
|
end
|
|
@@ -4,13 +4,16 @@ module Julewire
|
|
|
4
4
|
module Core
|
|
5
5
|
module Fields
|
|
6
6
|
module Internal
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
FIELD_KEY_ERROR = "field keys must be String or Symbol"
|
|
8
|
+
RECORD_STRING_KEY_ERROR = "record must not use string keys"
|
|
9
|
+
RECORD_SYMBOL_KEY_ERROR = "record keys must be Symbols"
|
|
10
10
|
|
|
11
11
|
class << self
|
|
12
12
|
def normalize_key(key)
|
|
13
|
-
key.is_a?(String)
|
|
13
|
+
return key.to_sym if key.is_a?(String)
|
|
14
|
+
return key if key.instance_of?(Symbol)
|
|
15
|
+
|
|
16
|
+
raise TypeError, FIELD_KEY_ERROR
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def delete_key!(target, key)
|
|
@@ -18,16 +21,19 @@ module Julewire
|
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def frozen_copy(value)
|
|
21
|
-
|
|
22
|
-
return EMPTY_ARRAY if value.is_a?(Array) && value.empty?
|
|
23
|
-
|
|
24
|
-
Serialization::ValueCopy.call(value, freeze_values: true)
|
|
24
|
+
frozen_copy_with(value, preserve_truncation_metadata: false)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def
|
|
28
|
-
|
|
27
|
+
def frozen_owned_copy(value)
|
|
28
|
+
frozen_copy_with(value, preserve_truncation_metadata: true)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def apply_delete_paths!(target, paths) = Deletion.apply_delete_paths!(target, paths)
|
|
32
|
+
|
|
33
|
+
def clear_delete_paths!(paths, fields) = Deletion.clear_delete_paths!(paths, fields)
|
|
34
|
+
|
|
35
|
+
def normalize_path(path) = Deletion.normalize_path(path)
|
|
36
|
+
|
|
31
37
|
def deep_merge!(target, fields)
|
|
32
38
|
merge_values!(target, fields) do |value, existing|
|
|
33
39
|
if existing.is_a?(Hash) && value.is_a?(Hash)
|
|
@@ -39,7 +45,7 @@ module Julewire
|
|
|
39
45
|
end
|
|
40
46
|
|
|
41
47
|
def deep_merge_owned!(target, fields)
|
|
42
|
-
|
|
48
|
+
merge_owned_values!(target, fields) do |value, existing|
|
|
43
49
|
if existing.is_a?(Hash) && value.is_a?(Hash)
|
|
44
50
|
deep_merge_owned!(existing, value)
|
|
45
51
|
else
|
|
@@ -49,26 +55,19 @@ module Julewire
|
|
|
49
55
|
end
|
|
50
56
|
|
|
51
57
|
def merge_owned!(target, fields)
|
|
52
|
-
|
|
58
|
+
merge_owned_values!(target, fields) { |value, _existing| value }
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
return EMPTY_HASH if value.is_a?(Hash) && value.empty?
|
|
57
|
-
return EMPTY_ARRAY if value.is_a?(Array) && value.empty?
|
|
61
|
+
private
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
def frozen_copy_with(value, preserve_truncation_metadata:)
|
|
64
|
+
Serialization::ValueCopy.call(
|
|
65
|
+
value,
|
|
66
|
+
freeze_values: true,
|
|
67
|
+
preserve_truncation_metadata: preserve_truncation_metadata
|
|
68
|
+
)
|
|
60
69
|
end
|
|
61
70
|
|
|
62
|
-
def delete_path!(target, path) = Deletion.delete_path!(target, path)
|
|
63
|
-
|
|
64
|
-
def apply_delete_paths!(target, paths) = Deletion.apply_delete_paths!(target, paths)
|
|
65
|
-
|
|
66
|
-
def clear_delete_paths!(paths, fields) = Deletion.clear_delete_paths!(paths, fields)
|
|
67
|
-
|
|
68
|
-
def normalize_path(path) = Deletion.normalize_path(path)
|
|
69
|
-
|
|
70
|
-
private
|
|
71
|
-
|
|
72
71
|
def merge_values!(target, fields)
|
|
73
72
|
return target unless fields.is_a?(Hash)
|
|
74
73
|
|
|
@@ -80,6 +79,16 @@ module Julewire
|
|
|
80
79
|
|
|
81
80
|
target
|
|
82
81
|
end
|
|
82
|
+
|
|
83
|
+
def merge_owned_values!(target, fields)
|
|
84
|
+
return target unless fields.is_a?(Hash)
|
|
85
|
+
|
|
86
|
+
fields.each do |key, value|
|
|
87
|
+
target[key] = yield value, target[key]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
target
|
|
91
|
+
end
|
|
83
92
|
end
|
|
84
93
|
end
|
|
85
94
|
end
|
|
@@ -8,25 +8,21 @@ module Julewire
|
|
|
8
8
|
def value(source, key)
|
|
9
9
|
return unless source.respond_to?(:[])
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
return direct unless direct.nil?
|
|
13
|
-
|
|
14
|
-
alternate_key(source, key)
|
|
11
|
+
source[key]
|
|
15
12
|
rescue StandardError
|
|
16
13
|
nil
|
|
17
14
|
end
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
# Decoded wire records have String keys, while in-process records use
|
|
17
|
+
# Symbols. Keep this conversion at presentation boundaries instead of
|
|
18
|
+
# making ordinary core lookups tolerant.
|
|
19
|
+
def wire_value(source, key)
|
|
20
|
+
result = value(source, key)
|
|
21
|
+
result.nil? ? value(source, key.to_s) : result
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def alternate_key(source, key)
|
|
26
|
-
case key
|
|
27
|
-
when Symbol then source[key.name]
|
|
28
|
-
when String then source[key.to_sym]
|
|
29
|
-
end
|
|
24
|
+
def blank?(value)
|
|
25
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
|
30
26
|
end
|
|
31
27
|
end
|
|
32
28
|
end
|
|
@@ -34,7 +34,7 @@ module Julewire
|
|
|
34
34
|
|
|
35
35
|
def to_h = section_hash
|
|
36
36
|
|
|
37
|
-
def [](key) =
|
|
37
|
+
def [](key) = call_store(:value, key, default: nil)
|
|
38
38
|
|
|
39
39
|
private
|
|
40
40
|
|
|
@@ -51,10 +51,6 @@ module Julewire
|
|
|
51
51
|
yield coerce_fields(fields, keyword_fields)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def nil_if_missing(value)
|
|
55
|
-
value.equal?(MISSING) ? nil : value
|
|
56
|
-
end
|
|
57
|
-
|
|
58
54
|
def add_section(fields)
|
|
59
55
|
call_store(:add, fields)
|
|
60
56
|
end
|
|
@@ -67,10 +63,6 @@ module Julewire
|
|
|
67
63
|
call_store(:hash)
|
|
68
64
|
end
|
|
69
65
|
|
|
70
|
-
def section_value(key, default:)
|
|
71
|
-
call_store(:value, key, default: default)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
66
|
def call_store(action, ...)
|
|
75
67
|
@store.public_send(store_method(action), ...)
|
|
76
68
|
end
|
|
@@ -4,13 +4,12 @@ module Julewire
|
|
|
4
4
|
module Core
|
|
5
5
|
module Fields
|
|
6
6
|
class StackSet
|
|
7
|
-
EMPTY_HASH = {}.freeze
|
|
8
|
-
private_constant :EMPTY_HASH
|
|
9
|
-
|
|
10
7
|
class << self
|
|
11
|
-
def inherit_from(source, inherit_attributes:
|
|
12
|
-
stacks = Bags.stack_sections.
|
|
13
|
-
|
|
8
|
+
def inherit_from(source, inherit_attributes:)
|
|
9
|
+
stacks = Bags.stack_sections.each_with_object({}) do |section, inherited|
|
|
10
|
+
next unless inherit_section?(section, inherit_attributes)
|
|
11
|
+
|
|
12
|
+
inherited[section] = source.stack(section).branch
|
|
14
13
|
end
|
|
15
14
|
new(**stacks)
|
|
16
15
|
end
|
|
@@ -20,16 +19,12 @@ module Julewire
|
|
|
20
19
|
def inherit_section?(section, inherit_attributes)
|
|
21
20
|
inherit_attributes || !%i[attributes neutral].include?(section)
|
|
22
21
|
end
|
|
23
|
-
|
|
24
|
-
def inherited_stack(source, section, inherit)
|
|
25
|
-
inherit ? source.stack(section).fork : FieldStack.new
|
|
26
|
-
end
|
|
27
22
|
end
|
|
28
23
|
|
|
29
24
|
def initialize(**sections)
|
|
30
25
|
@stacks = Bags.stack_sections.to_h do |section|
|
|
31
|
-
[section, field_stack(sections.fetch(section,
|
|
32
|
-
end
|
|
26
|
+
[section, field_stack(sections.fetch(section, nil), section)]
|
|
27
|
+
end
|
|
33
28
|
end
|
|
34
29
|
|
|
35
30
|
def stack(section)
|
|
@@ -48,8 +43,8 @@ module Julewire
|
|
|
48
43
|
stack(section).delete(path)
|
|
49
44
|
end
|
|
50
45
|
|
|
51
|
-
def with(section, fields, owned: false, &)
|
|
52
|
-
stack(section).with(fields, owned: owned, &)
|
|
46
|
+
def with(section, fields = nil, owned: false, **keyword_fields, &)
|
|
47
|
+
stack(section).with(fields, owned: owned, **keyword_fields, &)
|
|
53
48
|
end
|
|
54
49
|
|
|
55
50
|
def without(section, path, &)
|
|
@@ -28,7 +28,7 @@ module Julewire
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def reset!
|
|
31
|
-
@config =
|
|
31
|
+
@config = nil
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
private
|
|
@@ -38,9 +38,10 @@ module Julewire
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def validate_config!(configuration)
|
|
41
|
-
|
|
41
|
+
klass = configuration_class
|
|
42
|
+
return if configuration.is_a?(klass)
|
|
42
43
|
|
|
43
|
-
raise TypeError, "expected #{
|
|
44
|
+
raise TypeError, "expected #{klass}"
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def configuration_class
|
|
@@ -5,11 +5,11 @@ module Julewire
|
|
|
5
5
|
module Integration
|
|
6
6
|
# @api integration_spi
|
|
7
7
|
class DestinationHealth
|
|
8
|
-
def initialize(counter_keys:, failure_counter: :failures)
|
|
8
|
+
def initialize(counter_keys:, callback_failure_counter: nil, failure_counter: :failures)
|
|
9
9
|
@failure_counter = failure_counter
|
|
10
10
|
@state = Diagnostics::Health.new(
|
|
11
|
+
callback_failure_counter: callback_failure_counter,
|
|
11
12
|
counter_keys: counter_keys,
|
|
12
|
-
failure_counter: failure_counter,
|
|
13
13
|
track_failures: failure_counter == :failures
|
|
14
14
|
)
|
|
15
15
|
end
|
|
@@ -19,23 +19,44 @@ module Julewire
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def record_failure(error, counter: @failure_counter, **metadata)
|
|
22
|
-
@state.record_failure(error, counter: counter,
|
|
22
|
+
@state.record_failure(error, counter: counter, **metadata)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def record_loss(reason:, counter: reason, **metadata)
|
|
26
|
-
@state.record_loss(reason: reason, counter: counter,
|
|
26
|
+
@state.record_loss(reason: reason, counter: counter, **metadata)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
29
|
+
def record_callback_failure(callback_failure)
|
|
30
|
+
@state.record_callback_failure(callback_failure)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def degradation_marker = @state.degradation_marker
|
|
34
|
+
|
|
35
|
+
def clear_degradation_if_unchanged(marker) = @state.clear_degradation_if_unchanged(marker)
|
|
36
|
+
|
|
37
|
+
def recover_if_successful
|
|
38
|
+
marker = degradation_marker
|
|
39
|
+
result = yield
|
|
40
|
+
clear_degradation_if_unchanged(marker) unless result == false
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def clear_failures! = @state.clear_failures!
|
|
30
45
|
|
|
31
|
-
def degraded? = @state.degraded?
|
|
46
|
+
def degraded? = @state.degraded?
|
|
47
|
+
|
|
48
|
+
def last_callback_failure = @state.last_callback_failure
|
|
32
49
|
|
|
33
50
|
def last_loss = @state.last_loss
|
|
34
51
|
|
|
35
52
|
def last_failure = @state.last_failure
|
|
36
53
|
|
|
37
54
|
def snapshot(status: nil, **fields)
|
|
38
|
-
@state.snapshot(status: status,
|
|
55
|
+
snapshot = @state.snapshot(status: status, include_loss: true, **fields)
|
|
56
|
+
callback_failure = last_callback_failure
|
|
57
|
+
return snapshot unless callback_failure
|
|
58
|
+
|
|
59
|
+
snapshot.merge(last_callback_failure: callback_failure).freeze
|
|
39
60
|
end
|
|
40
61
|
end
|
|
41
62
|
end
|