| 1 |
module ActionController |
|---|
| 2 |
module Routing |
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
class RouteSet |
|---|
| 56 |
def recognize_path(path, environment={}) |
|---|
| 57 |
result = recognize_optimized(path, environment) and return result |
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } } |
|---|
| 61 |
|
|---|
| 62 |
if environment[:method] && !HTTP_METHODS.include?(environment[:method]) |
|---|
| 63 |
raise NotImplemented.new(*allows) |
|---|
| 64 |
elsif !allows.empty? |
|---|
| 65 |
raise MethodNotAllowed.new(*allows) |
|---|
| 66 |
else |
|---|
| 67 |
raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" |
|---|
| 68 |
end |
|---|
| 69 |
end |
|---|
| 70 |
|
|---|
| 71 |
def recognize_optimized(path, env) |
|---|
| 72 |
write_recognize_optimized |
|---|
| 73 |
recognize_optimized(path, env) |
|---|
| 74 |
end |
|---|
| 75 |
|
|---|
| 76 |
def write_recognize_optimized |
|---|
| 77 |
tree = segment_tree(routes) |
|---|
| 78 |
body = generate_code(tree) |
|---|
| 79 |
instance_eval %{ |
|---|
| 80 |
def recognize_optimized(path, env) |
|---|
| 81 |
segments = to_plain_segments(path) |
|---|
| 82 |
index = |
|---|
| 83 |
return nil unless index |
|---|
| 84 |
while index < routes.size |
|---|
| 85 |
result = routes[index].recognize(path, env) and return result |
|---|
| 86 |
index += 1 |
|---|
| 87 |
end |
|---|
| 88 |
nil |
|---|
| 89 |
end |
|---|
| 90 |
}, __FILE__, __LINE__ |
|---|
| 91 |
end |
|---|
| 92 |
|
|---|
| 93 |
def segment_tree(routes) |
|---|
| 94 |
tree = [0] |
|---|
| 95 |
|
|---|
| 96 |
i = -1 |
|---|
| 97 |
routes.each do |route| |
|---|
| 98 |
i += 1 |
|---|
| 99 |
|
|---|
| 100 |
segments = to_plain_segments(route.segments.inject("") { |str,s| str << s.to_s }) |
|---|
| 101 |
|
|---|
| 102 |
node = tree |
|---|
| 103 |
segments.each do |seg| |
|---|
| 104 |
seg = :dynamic if seg && seg[0] == ?: |
|---|
| 105 |
node << [seg, [i]] if node.empty? || node[node.size - 1][0] != seg |
|---|
| 106 |
node = node[node.size - 1][1] |
|---|
| 107 |
end |
|---|
| 108 |
end |
|---|
| 109 |
tree |
|---|
| 110 |
end |
|---|
| 111 |
|
|---|
| 112 |
def generate_code(list, padding=' ', level = 0) |
|---|
| 113 |
|
|---|
| 114 |
return padding + "#{list[0]}\n" if list.size == 1 && !(Array === list[0]) |
|---|
| 115 |
|
|---|
| 116 |
body = padding + "(seg = segments[#{level}]; \n" |
|---|
| 117 |
|
|---|
| 118 |
i = 0 |
|---|
| 119 |
was_nil = false |
|---|
| 120 |
list.each do |item| |
|---|
| 121 |
if Array === item |
|---|
| 122 |
i += 1 |
|---|
| 123 |
start = (i == 1) |
|---|
| 124 |
final = (i == list.size) |
|---|
| 125 |
tag, sub = item |
|---|
| 126 |
if tag == :dynamic |
|---|
| 127 |
body += padding + "#{start ? 'if' : 'elsif'} true\n" |
|---|
| 128 |
body += generate_code(sub, padding + " ", level + 1) |
|---|
| 129 |
break |
|---|
| 130 |
elsif tag == nil && !was_nil |
|---|
| 131 |
was_nil = true |
|---|
| 132 |
body += padding + "#{start ? 'if' : 'elsif'} seg.nil?\n" |
|---|
| 133 |
body += generate_code(sub, padding + " ", level + 1) |
|---|
| 134 |
else |
|---|
| 135 |
body += padding + "#{start ? 'if' : 'elsif'} seg == '#{tag}'\n" |
|---|
| 136 |
body += generate_code(sub, padding + " ", level + 1) |
|---|
| 137 |
end |
|---|
| 138 |
end |
|---|
| 139 |
end |
|---|
| 140 |
body += padding + "else\n" |
|---|
| 141 |
body += padding + " #{list[0]}\n" |
|---|
| 142 |
body += padding + "end)\n" |
|---|
| 143 |
body |
|---|
| 144 |
end |
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 |
def to_plain_segments(str) |
|---|
| 148 |
str = str.dup |
|---|
| 149 |
str.sub!(/^\/+/,'') |
|---|
| 150 |
str.sub!(/\/+$/,'') |
|---|
| 151 |
segments = str.split(/\.[^\/]+\/+|\/+|\.[^\/]+\Z/) |
|---|
| 152 |
segments << nil |
|---|
| 153 |
segments |
|---|
| 154 |
end |
|---|
| 155 |
|
|---|
| 156 |
end |
|---|
| 157 |
end |
|---|
| 158 |
end |
|---|