1 2 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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
module Rack::Mount
module CodeGeneration #:nodoc:
def _expired_recognize(env) #:nodoc:
raise 'route set not finalized'
end
def rehash
super
optimize_recognize!
end
private
def expire!
if @optimized_recognize_defined
remove_metaclass_method :recognize
class << self
alias_method :recognize, :_expired_recognize
end
@optimized_recognize_defined = false
end
super
end
def optimize_container_iterator(container)
Utils.debug "optimizing container - size #{container.size}"
body = []
container.each_with_index { |route, i|
body << "route = self[#{i}]"
body << 'matches = {}'
body << 'params = route.defaults.dup'
conditions = []
route.conditions.each do |(method, condition)|
b = []
if condition.is_a?(Regexp) || condition.respond_to?(:match)
if condition.is_a?(Regexp)
b << "if m = #{condition.inspect}.match(obj.#{method})"
b << "matches[:#{method}] = m"
elsif condition.class.instance_method(:match).arity == 1 ||
condition.class.instance_method(:match).arity <= -1
b << "condition = route.conditions[#{method.inspect}]"
b << "if m = condition.match(obj.#{method})"
b << "matches[:#{method}] = m"
else
raise 'unexpected arity for condition\'s :match method'
end
if (named_captures = route.named_captures[method]) && named_captures.any?
b << 'captures = m.captures'
b << 'p = nil'
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ')
end
else
b << "if m = obj.#{method} == route.conditions[:#{method}]"
end
b << 'true'
b << 'end'
conditions << "(#{b.join('; ')})"
end
body << <<-RUBY
if #{conditions.join(' && ')}
yield route, matches, params
end
RUBY
}
container.instance_eval(<<-RUBY, __FILE__, __LINE__)
def optimized_each(obj)
#{body.join("\n")}
nil
end
RUBY
end
def optimize_recognize!
Utils.debug "optimizing recognize"
uses_cache = false
keys = @recognition_keys.map { |key|
if key.respond_to?(:call_source)
uses_cache = true
key.call_source(:cache, :obj)
else
"obj.#{key}"
end
}.join(', ')
@optimized_recognize_defined = true
remove_metaclass_method :recognize
instance_eval(<<-RUBY, __FILE__, __LINE__)
def recognize(obj)
#{"cache = {}" if uses_cache}
container = @recognition_graph[#{keys}]
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
if block_given?
container.optimized_each(obj) do |route, matches, params|
yield route, matches, params
end
else
container.optimized_each(obj) do |route, matches, params|
return route, matches, params
end
end
nil
end
RUBY
end
# method_defined? can't distinguish between instance
# and meta methods. So we have to rescue if the method
# has not been defined in the metaclass yet.
def remove_metaclass_method(symbol)
metaclass = class << self; self; end
Utils.silence_debug { metaclass.send(:remove_method, symbol) }
rescue NameError
nil
end
end
end
|