lib/usher/node.rb in joshbuddy-usher-0.5.3 vs lib/usher/node.rb in joshbuddy-usher-0.5.4

- old
+ new

@@ -4,35 +4,52 @@ class Node Response = Struct.new(:path, :params, :remaining_path, :matched_path) - attr_reader :lookup, :greedy_lookup - attr_accessor :terminates, :exclusive_type, :parent, :value, :request_methods + attr_reader :normal, :greedy, :request + attr_accessor :terminates, :request_method_type, :parent, :value, :request_methods def initialize(parent, value) @parent = parent @value = value - @lookup = Hash.new - @greedy_lookup = Hash.new - @exclusive_type = nil + @request = nil + @normal = nil + @greedy = nil + @request_method_type = nil end + + def activate_normal! + @normal ||= Hash.new + end - def upgrade_lookup - @lookup = FuzzyHash.new(@lookup) + def activate_greedy! + @greedy ||= Hash.new end - def upgrade_greedy_lookup - @greedy_lookup = FuzzyHash.new(@greedy_lookup) + def activate_request! + @request ||= Hash.new end + def upgrade_normal! + @normal = FuzzyHash.new(@normal) + end + + def upgrade_greedy! + @greedy = FuzzyHash.new(@greedy) + end + + def upgrade_request! + @request = FuzzyHash.new(@request) + end + def depth @depth ||= @parent.is_a?(Node) ? @parent.depth + 1 : 0 end def greedy? - !@greedy_lookup.empty? + @greedy && !@greedy.empty? end def self.root(route_set, request_methods) root = self.new(route_set, nil) root.request_methods = request_methods @@ -43,16 +60,26 @@ @terminates end def pp $stdout << " " * depth - $stdout << "#{depth}: #{value.inspect} #{!!terminates?}\n" - @lookup.each do |k,v| + $stdout << "#{terminates? ? '* ' : ''}#{depth}: #{value.inspect}\n" + normal.each do |k,v| $stdout << " " * (depth + 1) - $stdout << "#{k} ==> \n" + $stdout << ". #{k.inspect} ==> \n" v.pp - end + end if normal + greedy.each do |k,v| + $stdout << " " * (depth + 1) + $stdout << "g #{k.inspect} ==> \n" + v.pp + end if greedy + request.each do |k,v| + $stdout << " " * (depth + 1) + $stdout << "r #{k.inspect} ==> \n" + v.pp + end if request end def add(route) route.paths.each do |path| set_path_with_destination(path) @@ -65,41 +92,42 @@ end end def unique_routes(node = self, routes = []) routes << node.terminates.route if node.terminates - node.lookup.values.each do |v| + node.normal.values.each do |v| unique_routes(v, routes) - end - node.greedy_lookup.values.each do |v| + end if node.normal + node.greedy.values.each do |v| unique_routes(v, routes) - end + end if node.greedy + node.request.values.each do |v| + unique_routes(v, routes) + end if node.request routes.uniq! routes end - def find(usher, request, original_path, path, params = [], position = 0) - if exclusive_type - [lookup[request.send(exclusive_type)], lookup[nil]].each do |n| - if n && (ret = n.find(usher, request, original_path, path.dup, params.dup, position)) - return ret - end - end - nil - elsif terminates? && (path.size.zero? || terminates.route.partial_match?) - if terminates.route.partial_match? - Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) + def find(usher, request_object, original_path, path, params = [], position = 0) + if request_method_type + if (specific_node = request[request_object.send(request_method_type)]) && (ret = specific_node.find(usher, request_object, original_path, path.dup, params.dup, position)) + ret + elsif (general_node = request[nil]) && (ret = general_node.find(usher, request_object, original_path, path.dup, params.dup, position)) + ret else - Response.new(terminates, params, nil, original_path) + nil end - - elsif !path.size.zero? && (greedy? && (match_with_result_output = greedy_lookup.match_with_result(whole_path = original_path[position, original_path.size]))) + elsif terminates? && (path.empty? || terminates.route.partial_match?) + terminates.route.partial_match? ? + Response.new(terminates, params, original_path[position, original_path.size], original_path[0, position]) : + Response.new(terminates, params, nil, original_path) + elsif !path.empty? && (greedy? && (match_with_result_output = greedy.match_with_result(whole_path = original_path[position, original_path.size]))) next_path, matched_part = match_with_result_output position += matched_part.size params << [next_path.value.name, whole_path.slice!(0, matched_part.size)] - next_path.find(usher, request, original_path, whole_path.size.zero? ? whole_path : usher.splitter.url_split(whole_path), params, position) - elsif !path.size.zero? && (next_part = lookup[part = path.shift] || lookup[nil]) + next_path.find(usher, request_object, original_path, whole_path.empty? ? whole_path : usher.splitter.url_split(whole_path), params, position) + elsif !path.empty? && normal && (next_part = normal[part = path.shift] || normal[nil]) position += part.size case next_part.value when Route::Variable::Glob params << [next_part.value.name, []] unless params.last && params.last.first == next_part.value.name loop do @@ -113,11 +141,11 @@ break elsif !usher.delimiter_chars.include?(part[0]) next_part.value.valid!(part) params.last.last << part end - if path.size.zero? + if path.empty? break else part = path.shift end end @@ -129,61 +157,109 @@ next_path_part = path.shift position += next_path_part.size params.last.last << next_path_part end end - next_part.find(usher, request, original_path, path, params, position) + next_part.find(usher, request_object, original_path, path, params, position) else nil end end private + def set_path_with_destination(path, destination = path) - parts = path.parts.dup - request_methods.each do |type| - parts.push(Route::RequestMethod.new(type, path.route.conditions[type])) if path.route.conditions && path.route.conditions.key?(type) + node = path.parts.inject(self){ |node, key| process_path_part(node, key) } + node = process_request_parts(node, request_methods_for_path(path)) + + while node.request_method_type + node = (node.request[nil] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil))) end - - current_node = self - while !parts.size.zero? || current_node.exclusive_type + + node.terminates = destination + end + + def request_method_index(type) + request_methods.index(type) + end + + def process_request_parts(node, parts) + while parts.any?{ |p| !p.trivial? } key = parts.shift - target_node = case key - when Route::RequestMethod - current_node.upgrade_lookup if key.value.is_a?(Regexp) - if current_node.exclusive_type == key.type - current_node.lookup[key.value] ||= Node.new(current_node, key) - elsif current_node.lookup.empty? - current_node.exclusive_type = key.type - current_node.lookup[key.value] ||= Node.new(current_node, key) - else + + next if key.trivial? + + node.activate_request! + + node = if node.request_method_type.nil? + node.request_method_type = key.type + node.upgrade_request! if key.value.is_a?(Regexp) + node.request[key.value] ||= Node.new(node, key) + else + case request_method_index(node.request_method_type) <=> request_method_index(key.type) + when -1 parts.unshift(key) - current_node.lookup[nil] ||= Node.new(current_node, Route::RequestMethod.new(current_node.exclusive_type, nil)) + node.request[key.value] ||= Node.new(node, Route::RequestMethod.new(node.request_method_type, nil)) + when 0 + node.upgrade_request! if key.value.is_a?(Regexp) + node.request[key.value] ||= Node.new(node, key) + when 1 + previous_node = node.parent + current_node_entry_key = nil + current_node_entry_lookup = nil + [previous_node.normal, previous_node.greedy, previous_node.request].compact.each do |l| + current_node_entry_key = l.each{|k,v| break k if node == v} + current_node_entry_lookup = l and break if current_node_entry_key + end + + current_node_entry_lookup.respond_to?(:delete_value) ? + current_node_entry_lookup.delete_value(node) : current_node_entry_lookup.delete_if{|k,v| v == node} + + new_node = Node.new(previous_node, Route::RequestMethod.new(key.type, nil)) + new_node.activate_request! + new_node.request_method_type = key.type + current_node_entry_lookup[current_node_entry_key] = new_node + node.parent = new_node + new_node.request[nil] = node + parts.unshift(key) + + new_node end - when Route::Variable - upgrade_method, lookup_method = case key - when Route::Variable::Greedy - [:upgrade_greedy_lookup, :greedy_lookup] - else - [:upgrade_lookup, :lookup] - end - - if key.regex_matcher - current_node.send(upgrade_method) - current_node.send(lookup_method)[key.regex_matcher] ||= Node.new(current_node, key) - else - current_node.send(lookup_method)[nil] ||= Node.new(current_node, key) - end - else - - current_node.upgrade_lookup if key.is_a?(Regexp) - current_node.lookup[key] ||= Node.new(current_node, key) end - current_node = target_node end + node + end - current_node.terminates = destination + + def process_path_part(node, key) + case key + when Route::Variable::Greedy + node.activate_greedy! + if key.regex_matcher + node.upgrade_greedy! + node.greedy[key.regex_matcher] ||= Node.new(node, key) + else + node.greedy[nil] ||= Node.new(node, key) + end + when Route::Variable + node.activate_normal! + if key.regex_matcher + node.upgrade_normal! + node.normal[key.regex_matcher] ||= Node.new(node, key) + else + node.normal[nil] ||= Node.new(node, key) + end + else + node.activate_normal! + node.upgrade_normal! if key.is_a?(Regexp) + node.normal[key] ||= Node.new(node, key) + end end + def request_methods_for_path(path) + request_methods.collect do |type| + Route::RequestMethod.new(type, path.route.conditions && path.route.conditions[type]) + end + end end end