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.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/docs/advanced-configuration.md +2 -1
  4. data/docs/configuration.md +14 -7
  5. data/docs/context-and-propagation.md +26 -4
  6. data/docs/contracts.md +25 -50
  7. data/docs/extensions-and-api.md +87 -88
  8. data/docs/health-schema.md +12 -2
  9. data/docs/internals.md +24 -23
  10. data/docs/outputs-and-lifecycle.md +15 -8
  11. data/docs/records-and-data-policy.md +38 -7
  12. data/lib/julewire/core/cli/doctor.rb +20 -16
  13. data/lib/julewire/core/cli/line_helpers.rb +23 -12
  14. data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
  15. data/lib/julewire/core/cli/log_formats/record_decoder.rb +1 -1
  16. data/lib/julewire/core/cli/log_formats.rb +9 -9
  17. data/lib/julewire/core/cli/tail.rb +19 -14
  18. data/lib/julewire/core/cli/transcode.rb +4 -5
  19. data/lib/julewire/core/cli.rb +1 -1
  20. data/lib/julewire/core/configuration.rb +1 -1
  21. data/lib/julewire/core/context_store.rb +56 -87
  22. data/lib/julewire/core/destinations/chaos_output.rb +2 -2
  23. data/lib/julewire/core/destinations/collection.rb +8 -16
  24. data/lib/julewire/core/destinations/definition.rb +1 -9
  25. data/lib/julewire/core/destinations/destination.rb +16 -66
  26. data/lib/julewire/core/destinations/processor_handling.rb +78 -0
  27. data/lib/julewire/core/destinations/registry.rb +1 -1
  28. data/lib/julewire/core/destinations/sink.rb +0 -1
  29. data/lib/julewire/core/destinations/synchronized_output.rb +54 -17
  30. data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
  31. data/lib/julewire/core/destinations/write_step.rb +5 -5
  32. data/lib/julewire/core/destinations.rb +0 -7
  33. data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
  34. data/lib/julewire/core/diagnostics/doctor.rb +22 -15
  35. data/lib/julewire/core/diagnostics/failure_snapshot.rb +9 -3
  36. data/lib/julewire/core/diagnostics/health.rb +41 -57
  37. data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
  38. data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
  39. data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
  40. data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
  41. data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
  42. data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
  43. data/lib/julewire/core/diagnostics/tail.rb +18 -22
  44. data/lib/julewire/core/execution/boundary.rb +2 -8
  45. data/lib/julewire/core/execution/handle.rb +14 -17
  46. data/lib/julewire/core/execution/lineage.rb +26 -62
  47. data/lib/julewire/core/execution/measurement_handle.rb +6 -10
  48. data/lib/julewire/core/execution/scope.rb +17 -22
  49. data/lib/julewire/core/execution/scope_fields.rb +4 -13
  50. data/lib/julewire/core/execution/scope_identity.rb +1 -9
  51. data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
  52. data/lib/julewire/core/execution/summary_state.rb +16 -19
  53. data/lib/julewire/core/execution/view.rb +8 -17
  54. data/lib/julewire/core/facade_methods.rb +67 -65
  55. data/lib/julewire/core/fields/attribute_keys.rb +0 -2
  56. data/lib/julewire/core/fields/bags.rb +23 -27
  57. data/lib/julewire/core/fields/field_set.rb +52 -16
  58. data/lib/julewire/core/fields/field_stack.rb +69 -109
  59. data/lib/julewire/core/fields/internal/deletion.rb +6 -9
  60. data/lib/julewire/core/fields/internal.rb +35 -26
  61. data/lib/julewire/core/fields/lookup.rb +9 -13
  62. data/lib/julewire/core/fields/section_proxy.rb +1 -9
  63. data/lib/julewire/core/fields/stack_set.rb +9 -14
  64. data/lib/julewire/core/fields/static_labels.rb +1 -1
  65. data/lib/julewire/core/integration/configurable.rb +4 -3
  66. data/lib/julewire/core/integration/destination_health.rb +28 -7
  67. data/lib/julewire/core/integration/event_subscriber.rb +1 -1
  68. data/lib/julewire/core/integration/facade.rb +12 -15
  69. data/lib/julewire/core/integration/fork_hooks.rb +12 -36
  70. data/lib/julewire/core/integration/health.rb +0 -3
  71. data/lib/julewire/core/integration/ivar_state.rb +0 -5
  72. data/lib/julewire/core/integration/protocol.rb +20 -0
  73. data/lib/julewire/core/integration/settings.rb +4 -4
  74. data/lib/julewire/core/integration/subscriber_install.rb +2 -2
  75. data/lib/julewire/core/integration/subscription.rb +3 -2
  76. data/lib/julewire/core/integration/values.rb +0 -17
  77. data/lib/julewire/core/local_storage.rb +3 -19
  78. data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
  79. data/lib/julewire/core/processing/level_threshold.rb +7 -14
  80. data/lib/julewire/core/processing/match.rb +6 -4
  81. data/lib/julewire/core/processing/pipeline.rb +46 -40
  82. data/lib/julewire/core/processing/processor_chain.rb +22 -11
  83. data/lib/julewire/core/processing/processor_registry.rb +3 -4
  84. data/lib/julewire/core/processing/record_field_transform.rb +0 -4
  85. data/lib/julewire/core/processing/sampling.rb +1 -3
  86. data/lib/julewire/core/processing.rb +2 -2
  87. data/lib/julewire/core/propagation/carrier.rb +73 -19
  88. data/lib/julewire/core/propagation.rb +18 -4
  89. data/lib/julewire/core/records/build_input.rb +75 -0
  90. data/lib/julewire/core/records/console_formatter.rb +6 -6
  91. data/lib/julewire/core/records/deconstruct.rb +1 -1
  92. data/lib/julewire/core/records/display_message.rb +2 -4
  93. data/lib/julewire/core/records/draft.rb +127 -258
  94. data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
  95. data/lib/julewire/core/records/metadata.rb +4 -3
  96. data/lib/julewire/core/records/public_projection.rb +2 -2
  97. data/lib/julewire/core/records/raw_input.rb +1 -1
  98. data/lib/julewire/core/records/record.rb +30 -18
  99. data/lib/julewire/core/records/severity.rb +7 -15
  100. data/lib/julewire/core/runtime.rb +87 -72
  101. data/lib/julewire/core/runtime_registry.rb +20 -15
  102. data/lib/julewire/core/runtime_state.rb +5 -3
  103. data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
  104. data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
  105. data/lib/julewire/core/serialization/bounded_transform.rb +3 -2
  106. data/lib/julewire/core/serialization/bounded_traversal.rb +72 -83
  107. data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
  108. data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
  109. data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
  110. data/lib/julewire/core/serialization/exception_shape.rb +7 -6
  111. data/lib/julewire/core/serialization/json_encoder.rb +10 -13
  112. data/lib/julewire/core/serialization/serializer.rb +22 -49
  113. data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
  114. data/lib/julewire/core/serialization/text_encoder.rb +5 -5
  115. data/lib/julewire/core/serialization/truncation_metadata.rb +157 -0
  116. data/lib/julewire/core/serialization/value_copy.rb +183 -109
  117. data/lib/julewire/core/serialization/value_traversal.rb +10 -127
  118. data/lib/julewire/core/testing.rb +6 -56
  119. data/lib/julewire/core/validation.rb +2 -3
  120. data/lib/julewire/core/version.rb +1 -1
  121. data/lib/julewire/core.rb +2 -7
  122. metadata +7 -19
  123. data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
  124. data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
  125. data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
  126. data/lib/julewire/core/testing/chaos/destination.rb +0 -55
  127. data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
  128. data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
  129. data/lib/julewire/core/testing/chaos.rb +0 -80
  130. data/lib/julewire/core/testing/contracts/component.rb +0 -162
  131. data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
  132. data/lib/julewire/core/testing/contracts/integration.rb +0 -166
  133. data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
  134. data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
  135. data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
  136. data/lib/julewire/core/testing/contracts/wire.rb +0 -60
  137. data/lib/julewire/core/testing/contracts.rb +0 -24
  138. data/lib/julewire/core/testing/coverage.rb +0 -58
  139. data/lib/julewire/core/testing/test_reports.rb +0 -78
@@ -15,16 +15,15 @@ module Julewire
15
15
  clean_hash(execution, RELATIONSHIP_KEYS)
16
16
  end
17
17
 
18
- def clean_owned_execution_hash(execution)
19
- clean_hash!(execution, RELATIONSHIP_KEYS)
18
+ def clean_normalized_lazy_relationship_hash(execution)
19
+ clean_normalized_hash(execution, LAZY_RELATIONSHIP_KEYS)
20
20
  end
21
21
 
22
- def clean_lazy_relationship_hash(execution)
23
- clean_hash(execution, LAZY_RELATIONSHIP_KEYS)
22
+ def clean_owned_execution_hash(execution)
23
+ clean_hash!(execution, RELATIONSHIP_KEYS)
24
24
  end
25
25
 
26
26
  def from_execution_hash(execution)
27
- execution = {} unless execution.is_a?(Hash)
28
27
  new(
29
28
  reference: execution_reference(execution),
30
29
  root_reference: relationship_value(execution, :root),
@@ -50,6 +49,11 @@ module Julewire
50
49
  clean_hash!(copy, keys)
51
50
  end
52
51
 
52
+ def clean_normalized_hash(execution, keys)
53
+ copy = execution.is_a?(Hash) ? execution.dup : {}
54
+ clean_hash!(copy, keys)
55
+ end
56
+
53
57
  def clean_hash!(copy, keys)
54
58
  keys.each do |key|
55
59
  Fields::Internal.delete_key!(copy, key)
@@ -63,7 +67,9 @@ module Julewire
63
67
  id = reference_value(execution, :id)
64
68
  reference[:type] = type unless type.equal?(MISSING)
65
69
  reference[:id] = id unless id.equal?(MISSING)
66
- reference.empty? ? nil : reference
70
+ return if reference.empty?
71
+
72
+ reference
67
73
  end
68
74
  end
69
75
 
@@ -76,15 +82,18 @@ module Julewire
76
82
  ancestors: nil,
77
83
  ancestors_truncated: false
78
84
  )
79
- @parent_lineage = parent_lineage
80
85
  @depth = depth_value(depth, parent_lineage)
81
- @root_reference = freeze_reference(root_reference || root_reference_for(reference, parent_lineage))
86
+ @root_reference = freeze_reference(
87
+ root_reference || parent_lineage&.root_reference_for_child || reference
88
+ )
82
89
  @parent_reference = freeze_reference(parent_reference)
83
90
  @ancestor_references = freeze_ancestors(ancestors)
84
91
  @ancestors_truncated = ancestors_truncated ? true : false
85
- @ancestor_references_for_child = nil
86
- @truncated = nil
87
- @truncated_computed = false
92
+ return if @ancestor_references
93
+
94
+ references = inherited_ancestor_references(parent_lineage)
95
+ @ancestor_references = references.last(MAX_ANCESTORS).freeze
96
+ @ancestors_truncated ||= references.length > MAX_ANCESTORS
88
97
  end
89
98
 
90
99
  def merge_into_frozen(execution)
@@ -95,34 +104,14 @@ module Julewire
95
104
  hash.freeze
96
105
  end
97
106
 
98
- def ancestors
99
- references = @ancestor_references_for_child
100
- return references if references
101
-
102
- materialize_ancestor_references_for_child
103
- end
104
-
105
- def truncated?
106
- return @truncated if @truncated_computed
107
+ def ancestors = @ancestor_references
107
108
 
108
- @truncated = @ancestors_truncated || ancestor_count_exceeds_limit?
109
- @truncated_computed = true
110
- @truncated
111
- end
112
-
113
- def freeze
114
- return self if frozen?
115
-
116
- ancestors
117
- truncated?
118
- @parent_lineage = nil
119
- super
120
- end
109
+ def truncated? = @ancestors_truncated
121
110
 
122
111
  protected
123
112
 
124
113
  def ancestor_references_for_child
125
- ancestors
114
+ @ancestor_references
126
115
  end
127
116
 
128
117
  def root_reference_for_child
@@ -131,37 +120,14 @@ module Julewire
131
120
 
132
121
  private
133
122
 
134
- attr_reader :parent_lineage
135
-
136
- def materialize_ancestor_references_for_child
137
- if @ancestor_references
138
- @ancestor_references_for_child = @ancestor_references.freeze
139
- else
140
- references = build_ancestor_references
141
- @truncated = references.length > MAX_ANCESTORS
142
- @truncated_computed = true
143
- @ancestor_references_for_child = references.last(MAX_ANCESTORS).freeze
144
- end
145
- end
146
-
147
- def ancestor_count_exceeds_limit?
148
- return false if @ancestor_references_for_child
149
-
150
- build_ancestor_references.length > MAX_ANCESTORS
151
- end
152
-
153
123
  def depth_value(depth, parent_lineage)
154
- return depth if depth.is_a?(Integer) && depth.positive?
124
+ return depth if depth.instance_of?(Integer) && depth.positive?
155
125
  return parent_lineage.depth + 1 if parent_lineage
156
126
 
157
127
  1
158
128
  end
159
129
 
160
- def root_reference_for(reference, parent_lineage)
161
- parent_lineage ? parent_lineage.root_reference_for_child : reference
162
- end
163
-
164
- def build_ancestor_references
130
+ def inherited_ancestor_references(parent_lineage)
165
131
  return [] unless parent_lineage && @parent_reference
166
132
 
167
133
  parent_lineage.ancestor_references_for_child + [@parent_reference]
@@ -175,15 +141,13 @@ module Julewire
175
141
 
176
142
  def freeze_reference(reference)
177
143
  return unless reference
178
- return reference if reference.frozen?
179
144
 
180
145
  Serialization::ValueCopy.call(reference, freeze_values: true)
181
146
  end
182
147
 
183
148
  def frozen_hash_copy(value)
184
149
  value.each_with_object({}) do |(key, field_value), copy|
185
- copy[Fields::Internal.normalize_key(key)] =
186
- Serialization::ValueCopy.call(field_value, freeze_values: true)
150
+ copy[key] = Serialization::ValueCopy.call(field_value, freeze_values: true)
187
151
  end
188
152
  end
189
153
  end
@@ -1,27 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent/atomic/atomic_boolean"
4
+
3
5
  module Julewire
4
6
  module Core
5
7
  module Execution
6
8
  class MeasurementHandle
7
9
  def initialize(&finish)
8
10
  @finish = finish
9
- @finished = false
10
- @mutex = Mutex.new
11
+ @finished = Concurrent::AtomicBoolean.new
11
12
  end
12
13
 
13
14
  def finish
14
- @mutex.synchronize do
15
- return if @finished
15
+ return unless @finished.make_true
16
16
 
17
- @finished = true
18
- @finish.call
19
- end
17
+ @finish.call
20
18
  end
21
19
 
22
- def finished?
23
- @mutex.synchronize { @finished }
24
- end
20
+ def finished? = @finished.true?
25
21
  end
26
22
  end
27
23
  end
@@ -4,14 +4,11 @@ module Julewire
4
4
  module Core
5
5
  module Execution
6
6
  class Scope
7
- EMPTY_HASH = {}.freeze
8
- private_constant :EMPTY_HASH
9
-
10
7
  attr_reader :finished_at
11
8
 
12
- def initialize(type:, id: nil, execution: EMPTY_HASH, execution_owned: false, summary_event: nil, # rubocop:disable Metrics/ParameterLists
13
- summary_severity: nil, summary_source: nil, attributes: EMPTY_HASH, carry: EMPTY_HASH,
14
- context: EMPTY_HASH, labels: EMPTY_HASH, neutral: EMPTY_HASH, parent: nil, started_at: nil)
9
+ def initialize(type:, id: nil, execution: nil, execution_owned: false, summary_event: nil, # rubocop:disable Metrics/ParameterLists
10
+ summary_severity: nil, summary_source: nil, attributes: nil, carry: nil,
11
+ context: nil, labels: nil, neutral: nil, parent: nil, started_at: nil)
15
12
  @identity = ScopeIdentity.new(
16
13
  type: type,
17
14
  id: id,
@@ -37,7 +34,7 @@ module Julewire
37
34
  def depth = @identity.depth
38
35
 
39
36
  def execution_hash
40
- Fields::FieldSet.deep_dup(frozen_execution_hash)
37
+ Fields::FieldSet.deep_dup_owned(frozen_execution_hash)
41
38
  end
42
39
 
43
40
  def frozen_execution_hash
@@ -45,7 +42,7 @@ module Julewire
45
42
  end
46
43
 
47
44
  def inheritable_execution_hash
48
- Fields::FieldSet.deep_dup(@execution)
45
+ Fields::FieldSet.deep_dup_owned(@execution)
49
46
  end
50
47
 
51
48
  def context_hash = @fields.context_hash
@@ -96,7 +93,7 @@ module Julewire
96
93
  end
97
94
  end
98
95
 
99
- def add_field(section, fields, owned: false)
96
+ def add_field(section, fields, owned:)
100
97
  @fields.add(section, fields, owned: owned)
101
98
  end
102
99
 
@@ -105,23 +102,22 @@ module Julewire
105
102
  @fields.delete(:carry, path)
106
103
  end
107
104
 
108
- def with_field(section, fields, owned: false, &)
109
- @fields.with(section, fields, owned: owned, &)
105
+ def with_field(section, fields, owned: false, **keyword_fields, &)
106
+ @fields.with(section, fields, owned: owned, **keyword_fields, &)
110
107
  end
111
108
 
112
- def with_context(fields, &)
113
- with_field(:context, fields, &)
109
+ def with_context(fields = nil, owned: false, **keyword_fields, &)
110
+ with_field(:context, fields, owned: owned, **keyword_fields, &)
114
111
  end
115
112
 
116
- def with_carry(fields, &)
117
- with_field(:carry, fields, &)
113
+ def with_carry(fields = nil, owned: false, **keyword_fields, &)
114
+ with_field(:carry, fields, owned: owned, **keyword_fields, &)
118
115
  end
119
116
 
120
117
  def without_carry(path, &)
121
- normalized_path = Fields::Internal.normalize_path(path)
122
- raise ArgumentError, "carry path is required" if normalized_path.empty?
118
+ raise ArgumentError, "carry path is required" if Fields::Internal.normalize_path(path).empty?
123
119
 
124
- @fields.without(:carry, normalized_path, &)
120
+ @fields.without(:carry, path, &)
125
121
  end
126
122
 
127
123
  def add_summary(fields, owned: false)
@@ -149,12 +145,12 @@ module Julewire
149
145
  end
150
146
 
151
147
  def summary_record_input
152
- @summary_state.record_input(**summary_record_fields(timestamp: finished_at || frozen_time(Time.now.utc)))
148
+ @summary_state.record_input(**summary_record_fields(timestamp: frozen_time(Time.now.utc)))
153
149
  end
154
150
 
155
151
  def owned_summary_record_input
156
152
  @summary_state.owned_record_input(
157
- **summary_record_fields(timestamp: finished_at || frozen_time(Time.now.utc))
153
+ **summary_record_fields(timestamp: frozen_time(Time.now.utc))
158
154
  )
159
155
  end
160
156
 
@@ -186,7 +182,7 @@ module Julewire
186
182
 
187
183
  def frozen_time(value) = @identity.frozen_time(value)
188
184
 
189
- def initialize_state(context:, attributes:, labels:, carry: {}, neutral: {})
185
+ def initialize_state(context:, attributes:, labels:, carry:, neutral:)
190
186
  @fields = ScopeFields.new(
191
187
  context: context,
192
188
  carry: carry,
@@ -194,7 +190,6 @@ module Julewire
194
190
  labels: labels,
195
191
  neutral: neutral
196
192
  )
197
- @finished_at = nil
198
193
  end
199
194
 
200
195
  def summary_state(event, severity, source)
@@ -4,9 +4,6 @@ module Julewire
4
4
  module Core
5
5
  module Execution
6
6
  class ScopeFields
7
- EMPTY_HASH = {}.freeze
8
- private_constant :EMPTY_HASH
9
-
10
7
  attr_reader :stacks
11
8
 
12
9
  def initialize(context:, attributes:, labels:, carry:, neutral:)
@@ -36,18 +33,14 @@ module Julewire
36
33
  end
37
34
 
38
35
  def labels_hash
39
- return {} if @labels.empty?
40
-
41
36
  Fields::FieldSet.deep_dup(@labels)
42
37
  end
43
38
 
44
39
  def frozen_labels_hash
45
- return EMPTY_HASH if @labels.empty?
46
-
47
40
  @frozen_labels_hash ||= Fields::Internal.frozen_copy(@labels)
48
41
  end
49
42
 
50
- def add(section, fields, owned: false)
43
+ def add(section, fields, owned:)
51
44
  @stacks.add(section, fields, owned: owned)
52
45
  end
53
46
 
@@ -55,8 +48,8 @@ module Julewire
55
48
  @stacks.delete(section, path)
56
49
  end
57
50
 
58
- def with(section, fields, owned: false, &)
59
- @stacks.with(section, fields, owned: owned, &)
51
+ def with(section, fields, owned:, **keyword_fields, &)
52
+ @stacks.with(section, fields, owned: owned, **keyword_fields, &)
60
53
  end
61
54
 
62
55
  def without(section, path, &)
@@ -66,9 +59,7 @@ module Julewire
66
59
  private
67
60
 
68
61
  def normalized_static_hash(value)
69
- return EMPTY_HASH if value.is_a?(Hash) && value.empty?
70
-
71
- Fields::FieldSet.deep_symbolize_keys(value)
62
+ Fields::FieldSet.deep_symbolize_keys(value || {})
72
63
  end
73
64
  end
74
65
  end
@@ -55,15 +55,7 @@ module Julewire
55
55
  end
56
56
 
57
57
  def freeze_identity_value(value)
58
- case value
59
- when String
60
- copy = value.frozen? ? value : value.dup
61
- copy.freeze
62
- when Symbol, Numeric, true, false, nil
63
- value
64
- else
65
- Serialization::ValueCopy.call(value, freeze_values: true)
66
- end
58
+ Serialization::ValueCopy.call(value, freeze_values: true)
67
59
  end
68
60
  end
69
61
  end
@@ -4,10 +4,8 @@ module Julewire
4
4
  module Core
5
5
  module Execution
6
6
  class ScopeSnapshot
7
- EMPTY_HASH = {}.freeze
8
- private_constant :EMPTY_HASH
9
-
10
- def initialize(execution: {}, carry: {}, attributes: {}, labels: {}, neutral: {}, lineage: nil)
7
+ def initialize(execution: {}, carry: {}, attributes: {}, labels: {}, neutral: {}, lineage: nil, owned: false)
8
+ @owned = owned
11
9
  @execution = normalized_hash(execution)
12
10
  @carry = normalized_hash(carry)
13
11
  @attributes = normalized_hash(attributes)
@@ -17,11 +15,11 @@ module Julewire
17
15
  end
18
16
 
19
17
  def execution_hash
20
- Fields::FieldSet.deep_dup(frozen_execution_hash)
18
+ Fields::FieldSet.deep_dup_owned(frozen_execution_hash)
21
19
  end
22
20
 
23
21
  def frozen_execution_hash
24
- @frozen_execution_hash ||= Fields::Internal.frozen_copy(@execution)
22
+ @frozen_execution_hash ||= Fields::Internal.frozen_owned_copy(@execution)
25
23
  end
26
24
 
27
25
  attr_reader :lineage
@@ -30,61 +28,42 @@ module Julewire
30
28
 
31
29
  def type = @execution[:type]
32
30
 
33
- def started_at = nil
31
+ def started_at; end
34
32
 
35
- def finished_at = nil
33
+ def finished_at; end
36
34
 
37
- def parent = nil
35
+ def parent; end
38
36
 
39
37
  def context_hash = {}
40
38
 
41
- def carry_hash
42
- return {} if @carry.empty?
43
-
44
- Fields::FieldSet.deep_dup(@carry)
45
- end
46
-
47
- def attributes_hash
48
- return {} if @attributes.empty?
39
+ def carry_hash = Fields::FieldSet.deep_dup_owned(@carry)
49
40
 
50
- Fields::FieldSet.deep_dup(@attributes)
51
- end
41
+ def attributes_hash = Fields::FieldSet.deep_dup_owned(@attributes)
52
42
 
53
- def neutral_hash
54
- return {} if @neutral.empty?
43
+ def neutral_hash = Fields::FieldSet.deep_dup_owned(@neutral)
55
44
 
56
- Fields::FieldSet.deep_dup(@neutral)
57
- end
58
-
59
- def labels_hash
60
- return {} if @labels.empty?
61
-
62
- Fields::FieldSet.deep_dup(@labels)
63
- end
45
+ def labels_hash = Fields::FieldSet.deep_dup_owned(@labels)
64
46
 
65
47
  def summary_hash = {}
66
48
 
67
49
  def metrics_hash = {}
68
50
 
69
- def frozen_labels_hash
70
- return EMPTY_HASH if @labels.empty?
71
-
72
- @frozen_labels_hash ||= Fields::Internal.frozen_copy(@labels)
73
- end
51
+ def frozen_labels_hash = (@frozen_labels_hash ||= Fields::Internal.frozen_owned_copy(@labels))
74
52
 
75
53
  def execution_reference_for_child
76
54
  reference = {}
77
- reference[:type] = @execution[:type] if @execution.key?(:type)
78
- reference[:id] = @execution[:id] if @execution.key?(:id)
79
- reference.empty? ? nil : Fields::Internal.frozen_copy(reference)
55
+ reference[:type] = @execution.fetch(:type) if @execution.key?(:type)
56
+ reference[:id] = @execution.fetch(:id) if @execution.key?(:id)
57
+ Fields::Internal.frozen_copy(reference) unless reference.empty?
80
58
  end
81
59
 
82
60
  private
83
61
 
84
62
  def normalized_hash(value)
85
- return EMPTY_HASH if value.is_a?(Hash) && value.empty?
63
+ return Fields::FieldSet.deep_symbolize_keys(value) unless @owned
86
64
 
87
- Fields::FieldSet.deep_symbolize_keys(value)
65
+ Serialization::DeepFreeze.validate_symbol_hash(value)
66
+ Fields::FieldSet.deep_dup_owned(value)
88
67
  end
89
68
  end
90
69
  end
@@ -16,31 +16,29 @@ module Julewire
16
16
  @attributes = {}
17
17
  @metrics = {}
18
18
  @errors = []
19
- @error_severity = nil
20
- @record_input = nil
21
19
  end
22
20
 
23
21
  def payload_hash
24
- Fields::FieldSet.deep_dup(@payload)
22
+ Fields::FieldSet.deep_dup_owned(@payload)
25
23
  end
26
24
 
27
25
  def metrics_hash
28
- Fields::FieldSet.deep_dup(@metrics)
26
+ Fields::FieldSet.deep_dup_owned(@metrics)
29
27
  end
30
28
 
31
- def add(fields, owned: false)
29
+ def add(fields, owned:)
32
30
  merge_fields!(@payload, fields, owned: owned)
33
31
  end
34
32
 
35
- def add_attributes(fields, owned: false)
33
+ def add_attributes(fields, owned:)
36
34
  deep_merge_fields!(@attributes, fields, owned: owned)
37
35
  end
38
36
 
39
- def add_neutral(fields, owned: false)
37
+ def add_neutral(fields, owned:)
40
38
  deep_merge_fields!(@neutral, fields, owned: owned)
41
39
  end
42
40
 
43
- def increment_attribute(path, by: 1)
41
+ def increment_attribute(path, by:)
44
42
  path = Fields::Internal.normalize_path(path)
45
43
  raise ArgumentError, "attribute path is required" if path.empty?
46
44
 
@@ -48,7 +46,7 @@ module Julewire
48
46
  key = path.last
49
47
  value = Fields::FieldSet.value_for(container, key, default: MISSING)
50
48
  existing = !value.equal?(MISSING)
51
- container[Fields::Internal.normalize_key(key)] = incremented_value(value, by, existing: existing)
49
+ container[key] = incremented_value(value, by, existing: existing)
52
50
  end
53
51
 
54
52
  def increment(key, by: 1)
@@ -66,7 +64,7 @@ module Julewire
66
64
  values << Fields::FieldSet.deep_dup(value)
67
65
  end
68
66
 
69
- def record_error(error, severity: nil)
67
+ def record_error(error, severity:)
70
68
  @errors << error
71
69
  @error_severity = Records::Severity.normalize(severity) unless severity.nil?
72
70
  end
@@ -90,7 +88,7 @@ module Julewire
90
88
  end
91
89
 
92
90
  def record_input(**fields)
93
- Fields::FieldSet.deep_dup(owned_record_input(**fields))
91
+ Fields::FieldSet.deep_dup_owned(owned_record_input(**fields))
94
92
  end
95
93
 
96
94
  def owned_record_input(**fields)
@@ -143,13 +141,13 @@ module Julewire
143
141
  def attributes_hash(base_attributes)
144
142
  return base_attributes if @attributes.empty?
145
143
 
146
- Fields::Internal.deep_merge(base_attributes, @attributes)
144
+ Fields::Internal.deep_merge_owned!(Fields::FieldSet.deep_dup_owned(base_attributes), @attributes)
147
145
  end
148
146
 
149
147
  def neutral_hash(base_neutral)
150
148
  return base_neutral if @neutral.empty?
151
149
 
152
- Fields::Internal.deep_merge(base_neutral, @neutral)
150
+ Fields::Internal.deep_merge_owned!(Fields::FieldSet.deep_dup_owned(base_neutral), @neutral)
153
151
  end
154
152
 
155
153
  def array_value(value, existing:)
@@ -173,23 +171,22 @@ module Julewire
173
171
  end
174
172
 
175
173
  def measurement_base(key)
176
- unless key.is_a?(String) || key.is_a?(Symbol)
174
+ unless key.is_a?(String) || key.instance_of?(Symbol)
177
175
  raise ArgumentError, "measurement key must be a String or Symbol"
178
176
  end
179
177
 
180
- base = key.to_s
178
+ base = key
181
179
  raise ArgumentError, "measurement key is required" if base.empty?
182
180
 
183
181
  base
184
182
  end
185
183
 
186
184
  def attribute_container(path)
187
- path[0...-1].reduce(@attributes) do |container, key|
188
- normalized = Fields::Internal.normalize_key(key)
189
- child = container[normalized]
185
+ path[...-1].reduce(@attributes) do |container, key|
186
+ child = container[key]
190
187
  unless child.is_a?(Hash)
191
188
  child = {}
192
- container[normalized] = child
189
+ container[key] = child
193
190
  end
194
191
  child
195
192
  end
@@ -16,15 +16,6 @@ module Julewire
16
16
  @finished_at = scope.finished_at
17
17
  @parent_scope = scope.parent
18
18
  @lineage = scope.lineage
19
- @parent = nil
20
- @execution_hash = nil
21
- @context_hash = nil
22
- @carry_hash = nil
23
- @neutral_hash = nil
24
- @attributes_hash = nil
25
- @labels_hash = nil
26
- @summary_hash = nil
27
- @metrics_hash = nil
28
19
  end
29
20
 
30
21
  def parent
@@ -33,21 +24,21 @@ module Julewire
33
24
  @parent ||= self.class.new(@parent_scope)
34
25
  end
35
26
 
36
- def execution_hash = Fields::FieldSet.deep_dup(@execution_hash ||= @scope.frozen_execution_hash)
27
+ def execution_hash = Fields::FieldSet.deep_dup_owned(@execution_hash ||= @scope.frozen_execution_hash)
37
28
 
38
- def context_hash = Fields::FieldSet.deep_dup(@context_hash ||= @scope.context_hash)
29
+ def context_hash = Fields::FieldSet.deep_dup_owned(@context_hash ||= @scope.context_hash)
39
30
 
40
- def carry_hash = Fields::FieldSet.deep_dup(@carry_hash ||= @scope.carry_hash)
31
+ def carry_hash = Fields::FieldSet.deep_dup_owned(@carry_hash ||= @scope.carry_hash)
41
32
 
42
- def neutral_hash = Fields::FieldSet.deep_dup(@neutral_hash ||= @scope.neutral_hash)
33
+ def neutral_hash = Fields::FieldSet.deep_dup_owned(@neutral_hash ||= @scope.neutral_hash)
43
34
 
44
- def attributes_hash = Fields::FieldSet.deep_dup(@attributes_hash ||= @scope.attributes_hash)
35
+ def attributes_hash = Fields::FieldSet.deep_dup_owned(@attributes_hash ||= @scope.attributes_hash)
45
36
 
46
- def labels_hash = Fields::FieldSet.deep_dup(@labels_hash ||= @scope.frozen_labels_hash)
37
+ def labels_hash = Fields::FieldSet.deep_dup_owned(@labels_hash ||= @scope.frozen_labels_hash)
47
38
 
48
- def summary_hash = Fields::FieldSet.deep_dup(@summary_hash ||= @scope.summary_hash)
39
+ def summary_hash = Fields::FieldSet.deep_dup_owned(@summary_hash ||= @scope.summary_hash)
49
40
 
50
- def metrics_hash = Fields::FieldSet.deep_dup(@metrics_hash ||= @scope.metrics_hash)
41
+ def metrics_hash = Fields::FieldSet.deep_dup_owned(@metrics_hash ||= @scope.metrics_hash)
51
42
 
52
43
  def finished? = !finished_at.nil?
53
44
  end