module ETL #:nodoc:
module Processor #:nodoc:
# Row-level processor that will convert a single row into multiple rows designed to be inserted
# into a hierarchy bridge table.
class HierarchyExploderProcessor < ETL::Processor::RowProcessor
attr_accessor :id_field
attr_accessor :parent_id_field
# Initialize the processor
#
# Configuration options:
# * :connection: The ActiveRecord adapter connection
# * :id_field: The name of the id field (defaults to 'id')
# * :parent_id_field: The name of the parent id field (defaults to 'parent_id')
#
# TODO: Allow resolver to be implemented in a customizable fashion, i.e. don't rely
# on AR as the only resolution method.
def initialize(control, configuration={})
@id_field = configuration[:id_field] || 'id'
@parent_id_field = configuration[:parent_id_field] || 'parent_id'
super
end
# Process the row expanding it into hierarchy values
def process(row)
rows = []
target = configuration[:target]
table = configuration[:table]
conn = ETL::Engine.connection(target)
build_rows([row[:id]], row[:id], row[:id], row[:parent_id].nil?, 0, rows, table, conn)
rows
end
protected
# Recursive function that will add a row for the current level and then call build_rows
# for all of the children of the current level
def build_rows(ids, parent_id, row_id, root, level, rows, table, conn)
ids.each do |id|
child_ids = conn.select_values("SELECT #{id_field} FROM #{table} WHERE #{parent_id_field} = #{id}")
row = {
:parent_id => row_id,
:child_id => id,
:num_levels_from_parent => level,
:is_bottom => (child_ids.empty? ? 1 : 0),
:is_top => (root ? 1 : 0),
}
rows << row
build_rows(child_ids, id, row_id, false, level + 1, rows, table, conn)
end
end
end
end
end