Skip to content

Commit

Permalink
[WIP] Ensure the tracking on nested and_then callings
Browse files Browse the repository at this point in the history
  • Loading branch information
serradura committed Dec 31, 2023
1 parent ca5d430 commit af66e41
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 149 deletions.
9 changes: 4 additions & 5 deletions lib/bcdd/result/transitions.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# frozen_string_literal: true

require 'securerandom'

class BCDD::Result
module Transitions
require_relative 'transitions/tree'
require_relative 'transitions/tracking'

THREAD_VAR_NAME = :bcdd_result_transitions_tracking
Expand All @@ -13,14 +12,14 @@ def self.tracking
end
end

def self.transitions(id: SecureRandom.uuid, name: nil, desc: nil)
Transitions.tracking.start(id: id, name: name, desc: desc)
def self.transitions(name: nil, desc: nil)
Transitions.tracking.start(name: name, desc: desc)

result = yield

result.is_a?(::BCDD::Result) or raise Error::UnexpectedOutcome.build(outcome: result, origin: :transitions)

Transitions.tracking.finish(id: id, result: result)
Transitions.tracking.finish(result: result)

result
rescue ::Exception => e
Expand Down
5 changes: 3 additions & 2 deletions lib/bcdd/result/transitions/tracking.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ module Tracking
require_relative 'tracking/enabled'
require_relative 'tracking/disabled'

EMPTY_HASH = {}.freeze
EMPTY_ARRAY = [].freeze
EMPTY_HASH = {}.freeze
EMPTY_TREE = Tree.new(nil).freeze
VERSION = 1
EMPTY = { records: EMPTY_ARRAY, version: VERSION }.freeze
EMPTY = { version: VERSION, records: EMPTY_ARRAY, metadata: { duration: 0, tree_map: EMPTY_ARRAY } }.freeze

def self.instance
Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
Expand Down
4 changes: 2 additions & 2 deletions lib/bcdd/result/transitions/tracking/disabled.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

module BCDD::Result::Transitions
module Tracking::Disabled
def self.start(id:, name:, desc:); end
def self.start(name:, desc:); end

def self.finish(id:, result:); end
def self.finish(result:); end

def self.reset!; end

Expand Down
76 changes: 34 additions & 42 deletions lib/bcdd/result/transitions/tracking/enabled.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,79 @@

module BCDD::Result::Transitions
class Tracking::Enabled
attr_accessor :root, :parent, :current, :parents, :records, :current_and_then
attr_accessor :tree, :records, :root_started_at

private :root, :root=, :parent, :parent=, :current, :current=
private :parents, :parents=, :records, :records=, :current_and_then, :current_and_then=
private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=

def start(id:, name:, desc:)
root.frozen? and return root_start(id, name, desc)
def start(name:, desc:)
name_and_desc = [name, desc]

self.parent = current if parent[:id] != current[:id]
self.current = { id: id, name: name, desc: desc }

parents[id] = parent
tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
end

def finish(id:, result:)
self.current = parents[id]
self.parent = parents.fetch(current[:id])
def finish(result:)
node = tree.current

tree.move_up!

return unless node.root?

return if root && root[:id] != id
duration = (now_in_milliseconds - root_started_at)

result.send(:transitions=, records: records, version: Tracking::VERSION)
metadata = { duration: duration, tree_map: tree.nested_ids }

result.send(:transitions=, version: Tracking::VERSION, records: records, metadata: metadata)

reset!
end

def reset!
self.root = Tracking::EMPTY_HASH
self.parent = Tracking::EMPTY_HASH
self.current = Tracking::EMPTY_HASH
self.parents = Tracking::EMPTY_HASH
self.records = Tracking::EMPTY_ARRAY

reset_current_and_then!
self.tree = Tracking::EMPTY_TREE
end

def record(result)
return if root.frozen?
return if tree.frozen?

track(result, time: ::Time.now.getutc)
end

def record_and_then(type_arg, arg, subject)
type = type_arg.instance_of?(::Method) ? :method : type_arg

self.current_and_then = { type: type, arg: arg, subject: subject }
unless tree.frozen?
current_and_then = { type: type, arg: arg, subject: subject }
current_and_then[:method_name] = type_arg.name if type == :method

current_and_then[:method_name] = type_arg.name if type == :method
tree.current.value[1] = current_and_then
end

result = yield

reset_current_and_then!

result
yield
end

private

def root_start(id, name, desc)
self.current = { id: id, name: name, desc: desc }
self.parent = current
self.root = current
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.parents = { current[:id] => current }
self.records = []

reset_current_and_then!
self.tree = Tree.new(name_and_desc, normalizer: TreeNodeValueNormalizer)
end

def track(result, time:)
result = result.data.to_h

and_then = current_and_then

record =
{ root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
root, = tree.root_value
parent, = tree.parent_value
current, and_then = tree.current_value

records << record
records << { root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
end

def reset_current_and_then!
self.current_and_then = Tracking::EMPTY_HASH
def now_in_milliseconds
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
end
end
end
95 changes: 95 additions & 0 deletions lib/bcdd/result/transitions/tree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# frozen_string_literal: true

class BCDD::Result
module Transitions
class Tree
class Node
attr_reader :id, :value, :parent, :normalizer, :children

def initialize(value, parent:, id:, normalizer:)
@normalizer = normalizer

@id = id
@value = normalizer.call(id, value)
@parent = parent

@children = []
end

def insert(value, id:)
node = self.class.new(value, parent: self, id: id, normalizer: normalizer)

@children << node

node
end

def root?
parent.nil?
end

def leaf?
children.empty?
end

def node?
!leaf?
end

def inspect
"#<#{self.class.name} id=#{id} children.size=#{children.size}>"
end
end

attr_reader :size, :root, :current

def initialize(value, normalizer: ->(_id, val) { val })
@size = 0

@root = Node.new(value, parent: nil, id: @size, normalizer: normalizer)

@current = @root
end

def root_value
root.value
end

def parent_value
current.parent&.value || root_value
end

def current_value
current.value
end

def insert(value)
@size += 1

current.insert(value, id: size)
end

def insert!(value)
@current = insert(value)
end

def move_up!(level = 1)
tap { level.times { @current = current.parent || root } }
end

def move_down!(level = 1, index: -1)
tap { level.times { current.children[index].then { |child| @current = child if child } } }
end

def move_to_root!
tap { @current = root }
end

NestedIds = ->(node) { [node.id, node.children.map(&NestedIds)] }

def nested_ids
NestedIds[root]
end
end
end
end
67 changes: 54 additions & 13 deletions sig/bcdd/result/transitions.rbs
Original file line number Diff line number Diff line change
@@ -1,38 +1,79 @@
class BCDD::Result
module Transitions
class Tree
class Node
attr_reader id: Integer
attr_reader value: untyped
attr_reader parent: (Node | nil)
attr_reader normalizer: ^(Integer, Array[untyped]) -> untyped
attr_reader children: Array[Node]

def initialize: (
untyped value,
parent: (Node | nil),
id: Integer,
normalizer: ^(Integer, Array[untyped]) -> untyped
) -> void

def insert: (untyped, id: Integer) -> Node

def root?: () -> bool
def leaf?: () -> bool
def node?: () -> bool
def inspect: () -> String
end

attr_reader size: Integer
attr_reader root: Node
attr_reader current: Node

def initialize: (untyped, ?normalizer: ^(Integer, Array[untyped]) -> untyped) -> void
def root_value: () -> untyped
def parent_value: () -> untyped
def current_value: () -> untyped
def insert: (untyped) -> Node
def insert!: (untyped) -> Node
def move_up!: (?Integer level) -> Tree
def move_down!: (?Integer level) -> Tree
def move_to_root!: () -> Tree

NestedIds: ^(Node) -> Array[untyped]

def nested_ids: () -> Array[untyped]
end

module Tracking
EMPTY_HASH: Hash[untyped, untyped]
EMPTY_ARRAY: Array[untyped]
EMPTY_HASH: Hash[untyped, untyped]
EMPTY_TREE: Transitions::Tree
VERSION: Integer
EMPTY: Hash[Symbol, untyped]


class Enabled
private attr_accessor root: Hash[Symbol, untyped]
private attr_accessor parent: Hash[Symbol, untyped]
private attr_accessor current: Hash[Symbol, untyped]
private attr_accessor parents: Hash[String, untyped]
private attr_accessor tree: Transitions::Tree
private attr_accessor records: Array[Hash[Symbol, untyped]]
private attr_accessor current_and_then: Hash[Symbol, untyped]
private attr_accessor root_started_at: Integer

def start: (id: String, name: String, desc: String) -> void
def finish: (id: String, result: BCDD::Result) -> void
def start: (name: String, desc: String) -> void
def finish: (result: BCDD::Result) -> void
def reset!: () -> void
def record: (BCDD::Result) -> void
def record_and_then: ((untyped), untyped, untyped) { () -> BCDD::Result } -> BCDD::Result

private

def root_start: (String id, String name, String desc) -> void
TreeNodeValueNormalizer: ^(Integer, Array[untyped]) -> untyped

def root_start: (Array[untyped]) -> void

def track: (BCDD::Result, time: Time) -> void

def reset_current_and_then!: () -> void
def now_in_milliseconds: () -> Integer
end

module Disabled
def self.start: (id: String, name: String, desc: String) -> void
def self.finish: (id: String, result: BCDD::Result) -> void
def self.start: (name: String, desc: String) -> void
def self.finish: (result: BCDD::Result) -> void
def self.reset!: () -> void
def self.record: (BCDD::Result) -> void
def self.record_and_then: ((untyped), untyped, untyped) { () -> BCDD::Result } -> BCDD::Result
Expand Down
Loading

0 comments on commit af66e41

Please sign in to comment.