lib/bcdd/result/transitions/tracking/enabled.rb in bcdd-result-0.12.0 vs lib/bcdd/result/transitions/tracking/enabled.rb in bcdd-result-0.13.0
- old
+ new
@@ -1,48 +1,68 @@
# frozen_string_literal: true
module BCDD::Result::Transitions
class Tracking::Enabled
- attr_accessor :tree, :records, :root_started_at
+ attr_accessor :tree, :records, :root_started_at, :listener
- private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=
+ private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=, :listener, :listener=
def exec(name, desc)
- start(name, desc)
+ transition_node, scope = start(name, desc)
- transition_node = tree.current
+ result = nil
- result = EnsureResult[yield]
+ listener.around_transitions(scope: scope) do
+ result = EnsureResult[yield]
+ end
tree.move_to_root! if transition_node.root?
finish(result)
result
+ rescue ::Exception => e
+ err!(e, transition_node)
end
+ def err!(exception, transition_node)
+ if transition_node.root?
+ listener.before_interruption(exception: exception, transitions: map_transitions)
+
+ reset!
+ end
+
+ raise exception
+ end
+
def reset!
self.tree = Tracking::EMPTY_TREE
end
def record(result)
return if tree.frozen?
track(result, time: ::Time.now.getutc)
end
- def record_and_then(type_arg, arg, source)
+ def record_and_then(type_arg, arg)
+ return yield if tree.frozen?
+
type = type_arg.instance_of?(::Method) ? :method : type_arg
- unless tree.frozen?
- current_and_then = { type: type, arg: arg, source: source }
- current_and_then[:method_name] = type_arg.name if type == :method
+ current_and_then = { type: type, arg: arg }
+ current_and_then[:method_name] = type_arg.name if type == :method
- tree.current.value[1] = current_and_then
- end
+ tree.current.value[1] = current_and_then
- yield
+ scope, and_then = tree.current_value
+
+ result = nil
+
+ listener.around_and_then(scope: scope, and_then: and_then) { result = yield }
+
+ result
end
def reset_and_then!
return if tree.frozen?
@@ -53,48 +73,87 @@
def start(name, desc)
name_and_desc = [name, desc]
tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
+
+ scope = tree.current.value[0]
+
+ listener.on_start(scope: scope)
+
+ [tree.current, scope]
end
def finish(result)
node = tree.current
tree.move_up!
return unless node.root?
- duration = (now_in_milliseconds - root_started_at)
+ transitions = map_transitions
- metadata = { duration: duration, tree_map: tree.nested_ids }
+ result.send(:transitions=, transitions)
- result.send(:transitions=, version: Tracking::VERSION, records: records, metadata: metadata)
+ listener.on_finish(transitions: transitions)
reset!
end
TreeNodeValueNormalizer = ->(id, (nam, des)) { [{ id: id, name: nam, desc: des }, Tracking::EMPTY_HASH] }
def root_start(name_and_desc)
self.root_started_at = now_in_milliseconds
+ self.listener = build_listener
+
self.records = []
self.tree = Tree.new(name_and_desc, normalizer: TreeNodeValueNormalizer)
end
def track(result, time:)
- result = result.data.to_h
+ record = track_record(result, time)
+ records << record
+
+ listener.on_record(record: record)
+
+ record
+ end
+
+ def track_record(result, time)
+ result_data = result.data.to_h
+ result_data[:source] = result.send(:source)
+
root, = tree.root_value
parent, = tree.parent_value
current, and_then = tree.current_value
- records << { root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
+ { root: root, parent: parent, current: current, result: result_data, and_then: and_then, time: time }
end
def now_in_milliseconds
::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
+ end
+
+ def map_transitions
+ duration = (now_in_milliseconds - root_started_at)
+
+ trace_id = Config.instance.trace_id.call
+
+ metadata = { duration: duration, ids_tree: tree.nested_ids, ids_matrix: tree.ids_matrix, trace_id: trace_id }
+
+ { version: Tracking::VERSION, records: records, metadata: metadata }
+ end
+
+ def build_listener
+ Config.instance.listener.new
+ rescue ::StandardError => e
+ err = "#{e.message} (#{e.class}); Backtrace: #{e.backtrace&.join(', ')}"
+
+ warn("Fallback to #{Listener::Null} because registered listener raised an exception: #{err}")
+
+ Listener::Null.new
end
end
end