dexkit 0.8.0 → 0.10.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 +36 -0
- data/README.md +50 -18
- data/gemfiles/mongoid_no_ar.gemfile +10 -0
- data/gemfiles/mongoid_no_ar.gemfile.lock +232 -0
- data/guides/llm/EVENT.md +41 -23
- data/guides/llm/FORM.md +202 -61
- data/guides/llm/OPERATION.md +49 -20
- data/guides/llm/QUERY.md +52 -2
- data/lib/dex/context_dsl.rb +56 -0
- data/lib/dex/context_setup.rb +2 -33
- data/lib/dex/event/bus.rb +85 -8
- data/lib/dex/event/handler.rb +18 -0
- data/lib/dex/event/metadata.rb +16 -9
- data/lib/dex/event/processor.rb +1 -1
- data/lib/dex/event/test_helpers.rb +88 -0
- data/lib/dex/event/trace.rb +14 -27
- data/lib/dex/event.rb +2 -7
- data/lib/dex/event_test_helpers.rb +1 -86
- data/lib/dex/form/context.rb +27 -0
- data/lib/dex/form/export.rb +128 -0
- data/lib/dex/form/nesting.rb +2 -0
- data/lib/dex/form/uniqueness_validator.rb +17 -1
- data/lib/dex/form.rb +119 -3
- data/lib/dex/id.rb +38 -0
- data/lib/dex/operation/async_proxy.rb +13 -2
- data/lib/dex/operation/explain.rb +11 -7
- data/lib/dex/operation/jobs.rb +5 -4
- data/lib/dex/operation/lock_wrapper.rb +15 -2
- data/lib/dex/operation/once_wrapper.rb +24 -15
- data/lib/dex/operation/record_backend.rb +15 -1
- data/lib/dex/operation/record_wrapper.rb +43 -8
- data/lib/dex/operation/test_helpers/assertions.rb +359 -0
- data/lib/dex/operation/test_helpers/execution.rb +30 -0
- data/lib/dex/operation/test_helpers/stubbing.rb +61 -0
- data/lib/dex/operation/test_helpers.rb +160 -0
- data/lib/dex/operation/trace_wrapper.rb +20 -0
- data/lib/dex/operation/transaction_adapter.rb +29 -68
- data/lib/dex/operation/transaction_wrapper.rb +10 -16
- data/lib/dex/operation.rb +2 -0
- data/lib/dex/query/backend.rb +13 -0
- data/lib/dex/query/export.rb +64 -0
- data/lib/dex/query.rb +50 -5
- data/lib/dex/ref_type.rb +4 -0
- data/lib/dex/test_helpers.rb +4 -139
- data/lib/dex/test_log.rb +62 -4
- data/lib/dex/trace.rb +291 -0
- data/lib/dex/type_coercion.rb +4 -1
- data/lib/dex/version.rb +1 -1
- data/lib/dexkit.rb +9 -5
- metadata +16 -5
- data/lib/dex/test_helpers/assertions.rb +0 -333
- data/lib/dex/test_helpers/execution.rb +0 -28
- data/lib/dex/test_helpers/stubbing.rb +0 -59
- /data/lib/dex/{event_test_helpers → event/test_helpers}/assertions.rb +0 -0
data/lib/dex/test_helpers.rb
CHANGED
|
@@ -1,148 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "
|
|
3
|
+
require_relative "operation/test_helpers"
|
|
4
|
+
require_relative "event/test_helpers"
|
|
4
5
|
|
|
5
6
|
module Dex
|
|
6
|
-
module TestWrapper
|
|
7
|
-
@_installed = false
|
|
8
|
-
|
|
9
|
-
class << self
|
|
10
|
-
def install!
|
|
11
|
-
return if @_installed
|
|
12
|
-
|
|
13
|
-
Dex::Operation.prepend(self)
|
|
14
|
-
@_installed = true
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def installed?
|
|
18
|
-
@_installed
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Stub registry
|
|
22
|
-
|
|
23
|
-
def stubs
|
|
24
|
-
@_stubs ||= {}
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def find_stub(klass)
|
|
28
|
-
stubs[klass]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def register_stub(klass, **options)
|
|
32
|
-
stubs[klass] = options
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def clear_stub(klass)
|
|
36
|
-
stubs.delete(klass)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def clear_all_stubs!
|
|
40
|
-
stubs.clear
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def call
|
|
45
|
-
stub = Dex::TestWrapper.find_stub(self.class)
|
|
46
|
-
return _test_apply_stub(stub) if stub
|
|
47
|
-
|
|
48
|
-
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
49
|
-
result = nil
|
|
50
|
-
err = nil
|
|
51
|
-
|
|
52
|
-
begin
|
|
53
|
-
result = super
|
|
54
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
55
|
-
err = e
|
|
56
|
-
raise
|
|
57
|
-
ensure
|
|
58
|
-
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
59
|
-
_test_record_to_log(result, err, duration)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
result
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
def _test_apply_stub(stub)
|
|
68
|
-
if stub[:error]
|
|
69
|
-
err_opts = stub[:error]
|
|
70
|
-
case err_opts
|
|
71
|
-
when Symbol
|
|
72
|
-
raise Dex::Error.new(err_opts)
|
|
73
|
-
when Hash
|
|
74
|
-
raise Dex::Error.new(err_opts[:code], err_opts[:message], details: err_opts[:details])
|
|
75
|
-
end
|
|
76
|
-
else
|
|
77
|
-
stub[:returns]
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def _test_safe_params
|
|
82
|
-
respond_to?(:to_h) ? to_h : {}
|
|
83
|
-
rescue
|
|
84
|
-
{}
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def _test_record_to_log(result, err, duration)
|
|
88
|
-
safe_result = if err
|
|
89
|
-
dex_err = if err.is_a?(Dex::Error)
|
|
90
|
-
err
|
|
91
|
-
else
|
|
92
|
-
Dex::Error.new(:exception, err.message, details: { exception_class: err.class.name })
|
|
93
|
-
end
|
|
94
|
-
Dex::Operation::Err.new(dex_err)
|
|
95
|
-
else
|
|
96
|
-
Dex::Operation::Ok.new(result)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
entry = Dex::TestLog::Entry.new(
|
|
100
|
-
type: "Operation",
|
|
101
|
-
name: self.class.name || self.class.to_s,
|
|
102
|
-
operation_class: self.class,
|
|
103
|
-
params: _test_safe_params,
|
|
104
|
-
result: safe_result,
|
|
105
|
-
duration: duration,
|
|
106
|
-
caller_location: caller_locations(4, 1)&.first
|
|
107
|
-
)
|
|
108
|
-
Dex::TestLog.record(entry)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
7
|
module TestHelpers
|
|
113
|
-
extend Dex::Concern
|
|
114
|
-
|
|
115
8
|
def self.included(base)
|
|
116
|
-
Dex::
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def setup
|
|
121
|
-
super
|
|
122
|
-
Dex::TestLog.clear!
|
|
123
|
-
Dex::TestWrapper.clear_all_stubs!
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
module ClassMethods
|
|
127
|
-
def testing(klass)
|
|
128
|
-
@_dex_test_subject = klass
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def _dex_test_subject
|
|
132
|
-
return @_dex_test_subject if defined?(@_dex_test_subject) && @_dex_test_subject
|
|
133
|
-
|
|
134
|
-
superclass._dex_test_subject if superclass.respond_to?(:_dex_test_subject)
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
private
|
|
139
|
-
|
|
140
|
-
def _dex_test_subject
|
|
141
|
-
self.class._dex_test_subject
|
|
9
|
+
base.include(Dex::Operation::TestHelpers)
|
|
10
|
+
base.include(Dex::Event::TestHelpers)
|
|
142
11
|
end
|
|
143
12
|
end
|
|
144
13
|
end
|
|
145
|
-
|
|
146
|
-
require_relative "test_helpers/execution"
|
|
147
|
-
require_relative "test_helpers/assertions"
|
|
148
|
-
require_relative "test_helpers/stubbing"
|
data/lib/dex/test_log.rb
CHANGED
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Dex
|
|
4
4
|
module TestLog
|
|
5
|
-
Entry = Data.define(
|
|
5
|
+
Entry = Data.define(
|
|
6
|
+
:type,
|
|
7
|
+
:name,
|
|
8
|
+
:operation_class,
|
|
9
|
+
:params,
|
|
10
|
+
:result,
|
|
11
|
+
:duration,
|
|
12
|
+
:caller_location,
|
|
13
|
+
:execution_id,
|
|
14
|
+
:trace_id,
|
|
15
|
+
:trace
|
|
16
|
+
)
|
|
6
17
|
|
|
7
18
|
@_entries = []
|
|
8
19
|
@_mutex = Mutex.new
|
|
@@ -42,14 +53,61 @@ module Dex
|
|
|
42
53
|
return "No operations called." if entries.empty?
|
|
43
54
|
|
|
44
55
|
lines = ["Operations called (#{entries.size}):"]
|
|
45
|
-
entries.each_with_index
|
|
56
|
+
nodes = entries.each_with_index.map { |entry, index| { entry: entry, index: index } }
|
|
57
|
+
by_id = nodes.to_h { |node| [node[:entry].execution_id, node] }
|
|
58
|
+
children = Hash.new { |hash, key| hash[key] = [] }
|
|
59
|
+
roots = []
|
|
60
|
+
|
|
61
|
+
nodes.each do |node|
|
|
62
|
+
parent_id = _parent_operation_id(node[:entry])
|
|
63
|
+
if parent_id && by_id[parent_id]
|
|
64
|
+
children[parent_id] << node
|
|
65
|
+
else
|
|
66
|
+
roots << node
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
counter = 0
|
|
71
|
+
render = lambda do |node, depth|
|
|
72
|
+
counter += 1
|
|
73
|
+
entry = node[:entry]
|
|
46
74
|
status = entry.result.ok? ? "OK" : "ERR(#{entry.result.code})"
|
|
47
75
|
duration_ms = entry.duration ? format("%.1fms", entry.duration * 1000) : "n/a"
|
|
48
|
-
|
|
49
|
-
|
|
76
|
+
indent = " " * depth
|
|
77
|
+
id = entry.execution_id ? " (#{_display_id(entry.execution_id)})" : ""
|
|
78
|
+
|
|
79
|
+
lines << " #{indent}#{counter}. #{entry.name}#{id} [#{status}] #{duration_ms}"
|
|
80
|
+
lines << " #{indent} params: #{entry.params.inspect}" unless entry.params.nil? || entry.params.empty?
|
|
81
|
+
|
|
82
|
+
children.fetch(entry.execution_id, []).sort_by { |child| child[:index] }.each do |child|
|
|
83
|
+
render.call(child, depth + 1)
|
|
84
|
+
end
|
|
50
85
|
end
|
|
86
|
+
|
|
87
|
+
roots.sort_by { |node| node[:index] }.each { |node| render.call(node, 0) }
|
|
51
88
|
lines.join("\n")
|
|
52
89
|
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def _frame_type(frame)
|
|
94
|
+
return unless frame
|
|
95
|
+
|
|
96
|
+
(frame[:type] || frame["type"])&.to_sym
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def _display_id(id)
|
|
100
|
+
prefix, suffix = id.to_s.split("_", 2)
|
|
101
|
+
return id.to_s unless suffix
|
|
102
|
+
|
|
103
|
+
"#{prefix}_#{suffix[0, 7]}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def _parent_operation_id(entry)
|
|
107
|
+
frames = Array(entry.trace)[0...-1]
|
|
108
|
+
parent = frames.reverse.find { |frame| _frame_type(frame) == :operation }
|
|
109
|
+
parent && (parent[:id] || parent["id"])
|
|
110
|
+
end
|
|
53
111
|
end
|
|
54
112
|
end
|
|
55
113
|
end
|
data/lib/dex/trace.rb
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dex
|
|
4
|
+
module Trace
|
|
5
|
+
FIBER_KEY = :_dex_trace
|
|
6
|
+
FRAME_TYPES = %i[actor operation handler].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def start(actor: nil, trace_id: nil)
|
|
10
|
+
previous = _dump_state
|
|
11
|
+
_set_state(trace_id: (trace_id || Dex::Id.generate("tr_")).to_s, frames: [], event_context: nil)
|
|
12
|
+
_state[:frames] << _normalize_actor(actor) if actor
|
|
13
|
+
yield
|
|
14
|
+
ensure
|
|
15
|
+
_restore_state(previous)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ensure_started!(trace_id: nil)
|
|
19
|
+
return false if active?
|
|
20
|
+
|
|
21
|
+
_state[:trace_id] = (trace_id || Dex::Id.generate("tr_")).to_s
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def active?
|
|
26
|
+
!trace_id.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def with_frame(frame)
|
|
30
|
+
auto_started = ensure_started!
|
|
31
|
+
pushed = false
|
|
32
|
+
push(frame)
|
|
33
|
+
pushed = true
|
|
34
|
+
yield
|
|
35
|
+
ensure
|
|
36
|
+
pop if pushed
|
|
37
|
+
stop! if auto_started
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def with_event_context(event)
|
|
41
|
+
auto_started = ensure_started!(trace_id: event.trace_id)
|
|
42
|
+
previous = _state[:event_context]
|
|
43
|
+
_state[:event_context] = _build_event_context(event)
|
|
44
|
+
yield
|
|
45
|
+
ensure
|
|
46
|
+
_state[:event_context] = previous
|
|
47
|
+
stop! if auto_started
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def restore_event_context(event_id:, trace_id:, event_class: nil, event_ancestry: [])
|
|
51
|
+
previous = _dump_state
|
|
52
|
+
effective_trace_id = trace_id&.to_s || _state[:trace_id]
|
|
53
|
+
_set_state(
|
|
54
|
+
trace_id: effective_trace_id,
|
|
55
|
+
frames: _normalize_frames(_state[:frames]),
|
|
56
|
+
event_context: {
|
|
57
|
+
id: event_id&.to_s,
|
|
58
|
+
trace_id: effective_trace_id,
|
|
59
|
+
event_class: event_class,
|
|
60
|
+
event_ancestry: Array(event_ancestry).compact.map(&:to_s)
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
yield
|
|
64
|
+
ensure
|
|
65
|
+
_restore_state(previous)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def push(frame)
|
|
69
|
+
_state[:frames] << _normalize_frame(frame)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def pop
|
|
73
|
+
_state[:frames].pop
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def current
|
|
77
|
+
_deep_copy(_state[:frames])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def trace_id
|
|
81
|
+
_state[:trace_id]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def current_id
|
|
85
|
+
_state[:frames].last&.dig(:id)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def actor
|
|
89
|
+
frame = _state[:frames].find { |entry| entry[:type] == :actor }
|
|
90
|
+
frame ? _deep_copy(frame) : nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def current_event_id
|
|
94
|
+
current_event_context&.dig(:id)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def current_event_context
|
|
98
|
+
context = _state[:event_context]
|
|
99
|
+
return _deep_copy(context) if context
|
|
100
|
+
|
|
101
|
+
handler = _state[:frames].reverse.find { |frame| frame[:type] == :handler && frame[:event_id] }
|
|
102
|
+
return nil unless handler
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
id: handler[:event_id],
|
|
106
|
+
trace_id: trace_id,
|
|
107
|
+
event_class: handler[:event_class],
|
|
108
|
+
event_ancestry: _deep_copy(Array(handler[:event_ancestry]))
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def dump
|
|
113
|
+
return nil unless active?
|
|
114
|
+
|
|
115
|
+
data = {
|
|
116
|
+
trace_id: trace_id,
|
|
117
|
+
frames: current
|
|
118
|
+
}
|
|
119
|
+
data[:event_context] = _deep_copy(_state[:event_context]) if _state[:event_context]
|
|
120
|
+
data
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def restore(data)
|
|
124
|
+
return yield unless data
|
|
125
|
+
|
|
126
|
+
previous = _dump_state
|
|
127
|
+
_set_state(
|
|
128
|
+
trace_id: _fetch(data, :trace_id)&.to_s,
|
|
129
|
+
frames: _normalize_frames(_fetch(data, :frames)),
|
|
130
|
+
event_context: _normalize_event_context(_fetch(data, :event_context))
|
|
131
|
+
)
|
|
132
|
+
yield
|
|
133
|
+
ensure
|
|
134
|
+
_restore_state(previous)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def to_s
|
|
138
|
+
current.filter_map { |frame| _format_frame(frame) }.join(" > ")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def clear!
|
|
142
|
+
Fiber[FIBER_KEY] = nil
|
|
143
|
+
end
|
|
144
|
+
alias_method :stop!, :clear!
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
def _state
|
|
149
|
+
Fiber[FIBER_KEY] ||= { trace_id: nil, frames: [], event_context: nil }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def _set_state(trace_id:, frames:, event_context:)
|
|
153
|
+
Fiber[FIBER_KEY] = {
|
|
154
|
+
trace_id: trace_id,
|
|
155
|
+
frames: frames,
|
|
156
|
+
event_context: event_context
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def _dump_state
|
|
161
|
+
state = _state
|
|
162
|
+
return nil unless state[:trace_id] || !state[:frames].empty? || state[:event_context]
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
trace_id: state[:trace_id],
|
|
166
|
+
frames: _deep_copy(state[:frames]),
|
|
167
|
+
event_context: _deep_copy(state[:event_context])
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def _restore_state(state)
|
|
172
|
+
if state
|
|
173
|
+
_set_state(
|
|
174
|
+
trace_id: state[:trace_id],
|
|
175
|
+
frames: _normalize_frames(state[:frames]),
|
|
176
|
+
event_context: _normalize_event_context(state[:event_context])
|
|
177
|
+
)
|
|
178
|
+
else
|
|
179
|
+
clear!
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def _normalize_frames(frames)
|
|
184
|
+
Array(frames).map { |frame| _normalize_frame(frame) }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def _normalize_actor(actor)
|
|
188
|
+
raise ArgumentError, "actor must be a Hash" unless actor.is_a?(Hash)
|
|
189
|
+
|
|
190
|
+
normalized = _symbolize(actor)
|
|
191
|
+
actor_type = normalized[:type]
|
|
192
|
+
raise ArgumentError, "actor must include :type" if actor_type.nil? || actor_type.to_s.strip.empty?
|
|
193
|
+
|
|
194
|
+
frame = { type: :actor, actor_type: actor_type.to_s }
|
|
195
|
+
frame[:id] = normalized[:id].to_s if normalized.key?(:id) && !normalized[:id].nil?
|
|
196
|
+
|
|
197
|
+
normalized.each do |key, value|
|
|
198
|
+
next if %i[type id].include?(key)
|
|
199
|
+
|
|
200
|
+
frame[key] = _deep_copy(value)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
frame
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def _normalize_frame(frame)
|
|
207
|
+
raise ArgumentError, "trace frame must be a Hash" unless frame.is_a?(Hash)
|
|
208
|
+
|
|
209
|
+
normalized = _symbolize(frame)
|
|
210
|
+
type = normalized[:type]&.to_sym
|
|
211
|
+
raise ArgumentError, "trace frame type is required" unless type
|
|
212
|
+
raise ArgumentError, "unknown trace frame type: #{type.inspect}" unless FRAME_TYPES.include?(type)
|
|
213
|
+
|
|
214
|
+
normalized[:type] = type
|
|
215
|
+
normalized[:id] = normalized[:id].to_s if normalized.key?(:id) && !normalized[:id].nil?
|
|
216
|
+
normalized[:actor_type] = normalized[:actor_type].to_s if normalized.key?(:actor_type) && !normalized[:actor_type].nil?
|
|
217
|
+
normalized[:event_id] = normalized[:event_id].to_s if normalized.key?(:event_id) && !normalized[:event_id].nil?
|
|
218
|
+
normalized[:event_ancestry] = Array(normalized[:event_ancestry]).compact.map(&:to_s) if normalized.key?(:event_ancestry)
|
|
219
|
+
normalized
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def _normalize_event_context(context)
|
|
223
|
+
return nil unless context
|
|
224
|
+
|
|
225
|
+
{
|
|
226
|
+
id: _fetch(context, :id)&.to_s,
|
|
227
|
+
trace_id: _fetch(context, :trace_id)&.to_s,
|
|
228
|
+
event_class: _fetch(context, :event_class),
|
|
229
|
+
event_ancestry: Array(_fetch(context, :event_ancestry)).compact.map(&:to_s)
|
|
230
|
+
}
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def _build_event_context(event)
|
|
234
|
+
{
|
|
235
|
+
id: event.id.to_s,
|
|
236
|
+
trace_id: event.trace_id.to_s,
|
|
237
|
+
event_class: event.class.name,
|
|
238
|
+
event_ancestry: Array(event.metadata.event_ancestry).compact.map(&:to_s)
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def _symbolize(hash)
|
|
243
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
244
|
+
result[key.respond_to?(:to_sym) ? key.to_sym : key] = _deep_copy(value)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def _deep_copy(value)
|
|
249
|
+
case value
|
|
250
|
+
when Hash
|
|
251
|
+
value.each_with_object({}) { |(key, nested), result| result[key] = _deep_copy(nested) }
|
|
252
|
+
when Array
|
|
253
|
+
value.map { |nested| _deep_copy(nested) }
|
|
254
|
+
else
|
|
255
|
+
value
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def _fetch(hash, key)
|
|
260
|
+
hash[key] || hash[key.to_s]
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def _format_frame(frame)
|
|
264
|
+
case frame[:type]
|
|
265
|
+
when :actor
|
|
266
|
+
return nil unless frame[:actor_type]
|
|
267
|
+
|
|
268
|
+
frame[:id] ? "#{frame[:actor_type]}:#{frame[:id]}" : frame[:actor_type]
|
|
269
|
+
when :operation
|
|
270
|
+
name = frame[:class] || "Operation"
|
|
271
|
+
id = _display_id(frame[:id])
|
|
272
|
+
id ? "#{name}(#{id})" : name
|
|
273
|
+
when :handler
|
|
274
|
+
event_class = frame[:event_class] || "Event"
|
|
275
|
+
handler_name = frame[:class] || "Handler"
|
|
276
|
+
id = _display_id(frame[:id])
|
|
277
|
+
id ? "[#{event_class}] #{handler_name}(#{id})" : "[#{event_class}] #{handler_name}"
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def _display_id(id)
|
|
282
|
+
return nil unless id
|
|
283
|
+
|
|
284
|
+
prefix, suffix = id.split("_", 2)
|
|
285
|
+
return id[0, 10] unless suffix
|
|
286
|
+
|
|
287
|
+
"#{prefix}_#{suffix[0, 7]}"
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
data/lib/dex/type_coercion.rb
CHANGED
|
@@ -64,7 +64,10 @@ module Dex
|
|
|
64
64
|
return _serialize_value(type.type, value) if type.is_a?(Literal::Types::NilableType)
|
|
65
65
|
|
|
66
66
|
ref = _find_ref_type(type)
|
|
67
|
-
|
|
67
|
+
if ref
|
|
68
|
+
serialized_id = value.id
|
|
69
|
+
return serialized_id.respond_to?(:as_json) ? serialized_id.as_json : serialized_id
|
|
70
|
+
end
|
|
68
71
|
|
|
69
72
|
value.respond_to?(:as_json) ? value.as_json : value
|
|
70
73
|
end
|
data/lib/dex/version.rb
CHANGED
data/lib/dexkit.rb
CHANGED
|
@@ -5,15 +5,12 @@ require "zeitwerk"
|
|
|
5
5
|
require "literal"
|
|
6
6
|
require "time"
|
|
7
7
|
|
|
8
|
-
loader = Zeitwerk::Loader.for_gem
|
|
9
|
-
loader.ignore("#{__dir__}/dex")
|
|
10
|
-
loader.setup
|
|
11
|
-
|
|
12
8
|
require_relative "dex/version"
|
|
13
9
|
require_relative "dex/concern"
|
|
14
10
|
require_relative "dex/ref_type"
|
|
15
11
|
require_relative "dex/type_coercion"
|
|
16
12
|
require_relative "dex/props_setup"
|
|
13
|
+
require_relative "dex/context_dsl"
|
|
17
14
|
require_relative "dex/context_setup"
|
|
18
15
|
require_relative "dex/error"
|
|
19
16
|
require_relative "dex/settings"
|
|
@@ -21,6 +18,8 @@ require_relative "dex/pipeline"
|
|
|
21
18
|
require_relative "dex/executable"
|
|
22
19
|
require_relative "dex/registry"
|
|
23
20
|
require_relative "dex/type_serializer"
|
|
21
|
+
require_relative "dex/id"
|
|
22
|
+
require_relative "dex/trace"
|
|
24
23
|
require_relative "dex/operation"
|
|
25
24
|
require_relative "dex/event"
|
|
26
25
|
require_relative "dex/tool"
|
|
@@ -29,7 +28,8 @@ require_relative "dex/query"
|
|
|
29
28
|
|
|
30
29
|
module Dex
|
|
31
30
|
class Configuration
|
|
32
|
-
attr_accessor :record_class, :
|
|
31
|
+
attr_accessor :record_class, :event_store, :event_context, :restore_event_context
|
|
32
|
+
attr_reader :transaction_adapter
|
|
33
33
|
|
|
34
34
|
def initialize
|
|
35
35
|
@record_class = nil
|
|
@@ -38,6 +38,10 @@ module Dex
|
|
|
38
38
|
@event_context = nil
|
|
39
39
|
@restore_event_context = nil
|
|
40
40
|
end
|
|
41
|
+
|
|
42
|
+
def transaction_adapter=(adapter)
|
|
43
|
+
@transaction_adapter = Operation::TransactionAdapter.normalize_name(adapter)
|
|
44
|
+
end
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
class << self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dexkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jacek Galanciak
|
|
@@ -161,11 +161,14 @@ files:
|
|
|
161
161
|
- LICENSE.txt
|
|
162
162
|
- README.md
|
|
163
163
|
- Untitled
|
|
164
|
+
- gemfiles/mongoid_no_ar.gemfile
|
|
165
|
+
- gemfiles/mongoid_no_ar.gemfile.lock
|
|
164
166
|
- guides/llm/EVENT.md
|
|
165
167
|
- guides/llm/FORM.md
|
|
166
168
|
- guides/llm/OPERATION.md
|
|
167
169
|
- guides/llm/QUERY.md
|
|
168
170
|
- lib/dex/concern.rb
|
|
171
|
+
- lib/dex/context_dsl.rb
|
|
169
172
|
- lib/dex/context_setup.rb
|
|
170
173
|
- lib/dex/error.rb
|
|
171
174
|
- lib/dex/event.rb
|
|
@@ -176,13 +179,17 @@ files:
|
|
|
176
179
|
- lib/dex/event/metadata.rb
|
|
177
180
|
- lib/dex/event/processor.rb
|
|
178
181
|
- lib/dex/event/suppression.rb
|
|
182
|
+
- lib/dex/event/test_helpers.rb
|
|
183
|
+
- lib/dex/event/test_helpers/assertions.rb
|
|
179
184
|
- lib/dex/event/trace.rb
|
|
180
185
|
- lib/dex/event_test_helpers.rb
|
|
181
|
-
- lib/dex/event_test_helpers/assertions.rb
|
|
182
186
|
- lib/dex/executable.rb
|
|
183
187
|
- lib/dex/form.rb
|
|
188
|
+
- lib/dex/form/context.rb
|
|
189
|
+
- lib/dex/form/export.rb
|
|
184
190
|
- lib/dex/form/nesting.rb
|
|
185
191
|
- lib/dex/form/uniqueness_validator.rb
|
|
192
|
+
- lib/dex/id.rb
|
|
186
193
|
- lib/dex/match.rb
|
|
187
194
|
- lib/dex/operation.rb
|
|
188
195
|
- lib/dex/operation/async_proxy.rb
|
|
@@ -200,12 +207,18 @@ files:
|
|
|
200
207
|
- lib/dex/operation/rescue_wrapper.rb
|
|
201
208
|
- lib/dex/operation/result_wrapper.rb
|
|
202
209
|
- lib/dex/operation/safe_wrapper.rb
|
|
210
|
+
- lib/dex/operation/test_helpers.rb
|
|
211
|
+
- lib/dex/operation/test_helpers/assertions.rb
|
|
212
|
+
- lib/dex/operation/test_helpers/execution.rb
|
|
213
|
+
- lib/dex/operation/test_helpers/stubbing.rb
|
|
214
|
+
- lib/dex/operation/trace_wrapper.rb
|
|
203
215
|
- lib/dex/operation/transaction_adapter.rb
|
|
204
216
|
- lib/dex/operation/transaction_wrapper.rb
|
|
205
217
|
- lib/dex/pipeline.rb
|
|
206
218
|
- lib/dex/props_setup.rb
|
|
207
219
|
- lib/dex/query.rb
|
|
208
220
|
- lib/dex/query/backend.rb
|
|
221
|
+
- lib/dex/query/export.rb
|
|
209
222
|
- lib/dex/query/filtering.rb
|
|
210
223
|
- lib/dex/query/sorting.rb
|
|
211
224
|
- lib/dex/railtie.rb
|
|
@@ -213,11 +226,9 @@ files:
|
|
|
213
226
|
- lib/dex/registry.rb
|
|
214
227
|
- lib/dex/settings.rb
|
|
215
228
|
- lib/dex/test_helpers.rb
|
|
216
|
-
- lib/dex/test_helpers/assertions.rb
|
|
217
|
-
- lib/dex/test_helpers/execution.rb
|
|
218
|
-
- lib/dex/test_helpers/stubbing.rb
|
|
219
229
|
- lib/dex/test_log.rb
|
|
220
230
|
- lib/dex/tool.rb
|
|
231
|
+
- lib/dex/trace.rb
|
|
221
232
|
- lib/dex/type_coercion.rb
|
|
222
233
|
- lib/dex/type_serializer.rb
|
|
223
234
|
- lib/dex/version.rb
|