# encoding: utf-8 module ActionDispatch module Journey # :nodoc: module Visitors # :nodoc: class Visitor # :nodoc: DISPATCH_CACHE = Hash.new { |h,k| h[k] = "visit_#{k}" } def accept node visit node end private def visit node send DISPATCH_CACHE[node.type], node end def binary node visit node.left visit node.right end def visit_CAT(n); binary(n); end def nary node node.children.each { |c| visit c } end def visit_OR(n); nary(n); end def unary node visit node.left end def visit_GROUP(n); unary(n); end def visit_STAR(n); unary(n); end def terminal node; end %w{ LITERAL SYMBOL SLASH DOT }.each do |t| class_eval %{ def visit_#{t}(n); terminal(n); end }, __FILE__, __LINE__ end end # Loop through the requirements AST class Each < Visitor # :nodoc: attr_reader :block def initialize block @block = block end def visit node super block.call node end end class String < Visitor # :nodoc: private def binary node [visit(node.left), visit(node.right)].join end def nary node node.children.map { |c| visit c }.join '|' end def terminal node node.left end def visit_GROUP node "(#{visit node.left})" end end # Used for formatting urls (url_for) class Formatter < Visitor # :nodoc: attr_reader :options, :consumed def initialize options @options = options @consumed = {} end private def visit_GROUP node if consumed == options nil else route = visit node.left route.include?("\0") ? nil : route end end def terminal node node.left end def binary node [visit(node.left), visit(node.right)].join end def nary node node.children.map { |c| visit c }.join end def visit_SYMBOL node key = node.to_sym if value = options[key] consumed[key] = value Router::Utils.escape_path(value) else "\0" end end end class Dot < Visitor # :nodoc: def initialize @nodes = [] @edges = [] end def accept node super <<-eodot digraph parse_tree { size="8,5" node [shape = none]; edge [dir = none]; #{@nodes.join "\n"} #{@edges.join("\n")} } eodot end private def binary node node.children.each do |c| @edges << "#{node.object_id} -> #{c.object_id};" end super end def nary node node.children.each do |c| @edges << "#{node.object_id} -> #{c.object_id};" end super end def unary node @edges << "#{node.object_id} -> #{node.left.object_id};" super end def visit_GROUP node @nodes << "#{node.object_id} [label=\"()\"];" super end def visit_CAT node @nodes << "#{node.object_id} [label=\"○\"];" super end def visit_STAR node @nodes << "#{node.object_id} [label=\"*\"];" super end def visit_OR node @nodes << "#{node.object_id} [label=\"|\"];" super end def terminal node value = node.left @nodes << "#{node.object_id} [label=\"#{value}\"];" end end end end end