require_relative './context_enhancer/arel_table' module Arel module Enhance # rubocop:disable Naming/MethodName class Visitor < Arel::Visitors::Dot DEFAULT_CONTEXT_ENHANCERS = { Arel::Table => Arel::Enhance::ContextEnhancer::ArelTable, }.freeze attr_reader :context_enhancers def accept(object, context_enhancers = DEFAULT_CONTEXT_ENHANCERS) @context_enhancers = context_enhancers root_node = Arel::Enhance::Node.new(object) accept_with_root(object, root_node) end def accept_with_root(object, root_node, context_enhancers = DEFAULT_CONTEXT_ENHANCERS) @context_enhancers = context_enhancers with_node(root_node) do visit object end root_node end private def visit_edge(object, method) arel_node = object.send(method) process_node(arel_node, Arel::Enhance::PathNode.new(method, method)) end def nary(object) visit_edge(object, 'children') end alias visit_Arel_Nodes_And nary def visit_Hash(object) object.each do |key, child| process_node(child, Arel::Enhance::PathNode.new([:[], key], key)) end end def visit_Array(object) object.each_with_index do |child, index| process_node(child, Arel::Enhance::PathNode.new([:[], index], index)) end end def process_node(arel_node, path_node) node = Arel::Enhance::Node.new(arel_node) current_node.add(path_node, node) update_context(node) with_node node do visit arel_node end end # rubocop:disable Metrics/AbcSize # arel/lib/arel/visitors/visitor.rb:29 def visit(object) dispatch_method = dispatch[object.class] send dispatch_method, object rescue NoMethodError => e raise e if respond_to?(dispatch_method, true) superklass = object.class.ancestors.find do |klass| respond_to?(dispatch[klass], true) end raise(TypeError, "Cannot visit #{object.class}") unless superklass dispatch[object.class] = dispatch[superklass] retry end # rubocop:enable Metrics/AbcSize def current_node @node_stack.last end def update_context(node) enhancer = context_enhancers[node.object.class] return if enhancer.nil? enhancer.call(node) end end # rubocop:enable Naming/MethodName end end