diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/journey/visitors.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/journey/visitors.rb | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb new file mode 100644 index 0000000000..46bd58c178 --- /dev/null +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -0,0 +1,189 @@ +# 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 |