app/lib/celery_script/slicer.rb
require_relative "./cs_heap.rb"
# ORIGINAL IMPLEMENTATION HERE: https://github.com/FarmBot-Labs/Celery-Slicer
# Take a nested ("canonical") representation of a CeleryScript sequence and
# transforms it to a flat/homogenous intermediate representation which is better
# suited for storage in a relational database.
module CeleryScript
class Slicer
attr_reader :root_node
def run!(node)
raise "Not a hash" unless node.is_a?(Hash)
@nesting_level = 0
@root_node = node
heap = CsHeap.new()
allocate(heap, node, CsHeap::NULL)
@heap_values = heap.values
@heap_values.map do |x|
x[CsHeap::BODY] ||= CsHeap::NULL
x[CsHeap::NEXT] ||= CsHeap::NULL
end
heap.dump()
end
def is_celery_script(node)
node && node.is_a?(Hash) && node[:args] && node[:kind]
end
def heap_values
@heap_values
end
def allocate(h, s, parentAddr)
addr = h.allot(s[:kind])
h.put(addr, CsHeap::PARENT, parentAddr)
h.put(addr, CsHeap::COMMENT, s[:comment]) if s[:comment]
iterate_over_body(h, s, addr)
iterate_over_args(h, s, addr)
addr
end
def iterate_over_args(h, s, parentAddr)
(s[:args] || {})
.keys
.map do |key|
v = s[:args][key]
if (is_celery_script(v))
k = CsHeap::LINK + key.to_s
h.put(parentAddr, k, allocate(h, v, parentAddr))
else
h.put(parentAddr, key, v)
end
end
end
def iterate_over_body(heap, canonical_node, parentAddr)
body = (canonical_node[:body] || []).map(&:deep_symbolize_keys)
@nesting_level += 1
recurse_into_body(heap, body, parentAddr)
@nesting_level -= 1
end
def recurse_into_body(heap, canonical_list, previous_address, index = 0)
if canonical_list[index]
is_head = index == 0
# BE CAREFUL EDITING THIS LINE, YOU MIGHT BREAK `BODY` NODES:
heap # See note above!
.put(previous_address, CsHeap::BODY, previous_address + 1) if is_head
my_heap_address = allocate(heap, canonical_list[index], previous_address)
prev_next_key = is_head ? CsHeap::NULL : my_heap_address
heap.put(previous_address, CsHeap::NEXT, prev_next_key)
recurse_into_body(heap, canonical_list, my_heap_address, index + 1)
end
end
end
end