aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/journey/visitors.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/journey/visitors.rb')
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb95
1 files changed, 65 insertions, 30 deletions
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 2964d80d9f..d9f634623d 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -1,10 +1,13 @@
# encoding: utf-8
+
+require 'thread_safe'
+
module ActionDispatch
module Journey # :nodoc:
module Visitors # :nodoc:
class Visitor # :nodoc:
- DISPATCH_CACHE = Hash.new { |h,k|
- h[k] = "visit_#{k}"
+ DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k|
+ h[k] = :"visit_#{k}"
}
def accept(node)
@@ -74,54 +77,86 @@ module ActionDispatch
end
end
- class OptimizedPath < String # :nodoc:
+ class OptimizedPath < Visitor # :nodoc:
+ def accept(node)
+ Array(visit(node))
+ end
+
private
- def visit_GROUP(node)
- ""
- end
+ def visit_CAT(node)
+ [visit(node.left), visit(node.right)].flatten
+ end
+
+ def visit_SYMBOL(node)
+ node.left[1..-1].to_sym
+ end
+
+ def visit_STAR(node)
+ visit(node.left)
+ end
+
+ def visit_GROUP(node)
+ []
+ end
+
+ %w{ LITERAL SLASH DOT }.each do |t|
+ class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__
+ end
end
# Used for formatting urls (url_for)
class Formatter < Visitor # :nodoc:
- attr_reader :options, :consumed
+ attr_reader :options
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
+ def escape_path(value)
+ Router::Utils.escape_path(value)
+ end
+
+ def escape_segment(value)
+ Router::Utils.escape_segment(value)
+ end
+
+ def visit(node, optional = false)
+ case node.type
+ when :LITERAL, :SLASH, :DOT
+ node.left
+ when :STAR
+ visit_STAR(node.left)
+ when :GROUP
+ visit(node.left, true)
+ when :CAT
+ visit_CAT(node, optional)
+ when :SYMBOL
+ visit_SYMBOL(node, node.to_sym)
end
end
- def terminal(node)
- node.left
- end
+ def visit_CAT(node, optional)
+ left = visit(node.left, optional)
+ right = visit(node.right, optional)
- def binary(node)
- [visit(node.left), visit(node.right)].join
+ if optional && !(right && left)
+ ""
+ else
+ [left, right].join
+ end
end
- def nary(node)
- node.children.map { |c| visit(c) }.join
+ def visit_STAR(node)
+ if value = options[node.to_sym]
+ escape_path(value)
+ end
end
- def visit_SYMBOL(node)
- key = node.to_sym
-
- if value = options[key]
- consumed[key] = value
- Router::Utils.escape_path(value)
- else
- "\0"
+ def visit_SYMBOL(node, name)
+ if value = options[name]
+ name == :controller ? escape_path(value) : escape_segment(value)
end
end
end